/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       town.cpp

	$Header: /game/town.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "town.h"

#include <limits>
#include <math.h>

#include "abstract_adventure_map.h"
#include "abstract_script_action.h"
#include "adventure_ai.h"
#include "adventure_frame.h"
#include "adventure_map.h"
#include "adventure_map_window.h"
#include "adventure_sounds.h"
#include "adv_mana_vortex.h"
#include "adv_object_model.h"
#include "adv_object_strings.h"
#include "ai_town_data_cache.h"
#include "alignment_relationship.h"
#include "army.h"
#include "army_array.h"
#include "army_info_window.h"
#include "basic_dialog.h"
#include "building_traits.h"
#include "caravan.h"
#include "caravan_set.h"
#include "combat_context_adv_object.h"
#include "combat_context_ptr.h"
#include "combat_result.h"
#include "counted_ptr.h"
#include "creature.h"
#include "creature_traits.h"
#include "creature_type.h"
#include "dialog_magic_gem.h"
#include "elements_of.h"
#include "enum_operations.h"
#include "external_string.h"
#include "float_object.h"
#include "game_replace_keywords.h"
#include "hero.h"
#include "hero_keyword_replacer.h"
#include "map_rect.h"
#include "material_names.h"
#include "music.h"
#include "object_registration.h"
#include "ownable_event.h"
#include "player.h"
#include "random.h"
#include "resurrect_heroes.h"
#include "run_combat.h"
#include "simple_dialog.h"
#include "sized_int_types.h"
#include "skill.h"
#include "skill_properties.h"
#include "skill_set.h"
#include "spell.h"
#include "spell_list.h"
#include "spell_properties.h"
#include "streambuf_operators.h"
#include "town_school_skills.h"
#include "script_targeting.h"

// Default values for create_spells()
extern t_spell_set const k_town_default_required_spells = t_spell_set();
extern t_spell_set const k_town_default_potential_spells = ~t_spell_set();

namespace
{
	int const k_placement_search_range = 3;	// How far to look for a place to put a ship

	int const k_current_map_format_version = 4;

	int const k_school_skill_count = 4;

	// Amount treasury adds per week
	double const k_per_week_treasury_growth = 0.1;

	// Amount treasury adds per day. Should come out to 
	// k_per_week_treasury_growth if left alone for 7 days
	double const k_per_day_treasury_growth = pow( 1.0 + k_per_week_treasury_growth, (1.0 / 7.0 ) ) - 1.0;

	class t_requires;

	typedef t_counted_ptr<t_requires> t_requirement_ptr;

	class t_requires : public t_counted_object
	{
	public:
		virtual void		ai_distribute_value( t_town const* town, float distribute_amount, float* values );
		virtual t_requirement_ptr and( t_requires* arg);
		virtual t_requirement_ptr clone() const = 0;
		virtual bool        enabled( t_town const* town ) const = 0;
		virtual std::string	get_text( t_town const* town, bool& use_prefix, t_town_building this_building ) const = 0;
		virtual bool        test( t_town const* town ) const = 0;
	};

	class t_requires_nothing : public t_requires
	{
	public:
		virtual t_requirement_ptr and( t_requires* arg);
		virtual t_requirement_ptr clone() const;
		virtual bool        enabled( t_town const* town ) const;
		virtual std::string get_text( t_town const* town, bool& use_prefix, t_town_building this_building ) const;
		virtual bool        test( t_town const* town ) const;
	};

	class t_requires_building : public t_requires
	{
	public:
		t_requires_building( t_town_building building );

		virtual void		ai_distribute_value( t_town const* town, float distribute_amount, float* values );
		virtual t_requirement_ptr clone() const;
		virtual bool        enabled( t_town const* town ) const;
		virtual std::string get_text( t_town const* town, bool& use_prefix, t_town_building this_building ) const;
		virtual bool        test( t_town const* town ) const;
	protected:
		t_town_building   m_building;
	};

	class t_or : public t_requires
	{
	public:
		t_or( t_requirement_ptr left, t_requirement_ptr right );

		virtual void		ai_distribute_value( t_town const* town, float distribute_amount, float* values );
		virtual t_requirement_ptr clone() const;
		virtual bool        enabled( t_town const* town ) const;
		virtual std::string get_text( t_town const* town, bool& use_prefix, t_town_building this_building ) const;
		virtual bool        test( t_town const* town ) const;
	protected:
		t_requirement_ptr m_left;
		t_requirement_ptr m_right;
	};


	class t_and : public t_requires
	{
	public:
		virtual void		ai_distribute_value( t_town const* town, float distribute_amount, float* values );
		virtual t_requirement_ptr and( t_requires* arg);
		virtual t_requirement_ptr clone() const;
		virtual bool        enabled( t_town const* town ) const;
		virtual std::string get_text( t_town const* town, bool& use_prefix, t_town_building this_building ) const;
		virtual bool        test( t_town const* town ) const;
	protected:
		std::vector<t_requirement_ptr> m_requirements;
	};

	class t_not : public t_requires
	{
	public:
		t_not( t_town_building building );

		virtual t_requirement_ptr clone() const;
		virtual bool        enabled( t_town const* town ) const;
		virtual std::string get_text( t_town const* town, bool& use_prefix, t_town_building this_building ) const;
		virtual bool        test( t_town const* town ) const;
	protected:
		t_town_building m_building;
	};

	t_requirement_ptr   k_requirements[k_town_type_count][k_town_building_count];
	t_town_building_set	k_legal[k_town_type_count];

	class t_initializer
	{
	public:
		t_initializer();

		void common( t_requirement_ptr requirements[] );
		void life();
		void order();
		void death();
		void chaos();
		void nature();
		void might();
		void magic_guild( t_requirement_ptr requirements[] );
		void set_legal( t_town_building_set& legal, t_requirement_ptr requirements[] );
	protected:
		t_requirement_ptr m_village_hall;
		t_requirement_ptr m_town_hall;
		t_requirement_ptr m_city_hall;
		t_requirement_ptr m_fort;
		t_requirement_ptr m_citadel;
		t_requirement_ptr m_castle;
		t_requirement_ptr m_caravan;
		t_requirement_ptr m_tavern;
		t_requirement_ptr m_blacksmith;
		t_requirement_ptr m_mage_guild_1;
		t_requirement_ptr m_mage_guild_2;
		t_requirement_ptr m_mage_guild_3;
		t_requirement_ptr m_mage_guild_4;
		t_requirement_ptr m_mage_guild_5;
		t_requirement_ptr m_library_1;
		t_requirement_ptr m_library_2;
	};

	t_register_with_type< t_town >		town_registration( k_adv_object_town );
	t_object_registration< t_outpost >	outpost_registration( k_adv_object_outpost );



}; // unnamed namespace

// -----------------------------------------------------------
// initialize requirements
// -----------------------------------------------------------
static t_requirement_ptr operator ||( t_requirement_ptr left, t_requirement_ptr right )
{
	return new t_or( left, right );
}

// -----------------------------------------------------------
// initialize requirements
// -----------------------------------------------------------
static t_requirement_ptr operator &&( t_requirement_ptr left, t_requirement_ptr right )
{
	t_requirement_ptr result = left->clone();

	return result->and( right );
}

static t_requirement_ptr not( t_town_building arg )
{
	return new t_not( arg );
};

// -----------------------------------------------------------
// initialize requirements
// -----------------------------------------------------------
t_initializer::t_initializer()
{
	m_village_hall = new t_requires_building( k_town_village_hall );
	m_town_hall		= new t_requires_building( k_town_hall );
	m_city_hall		= new t_requires_building( k_town_city_hall );
	m_fort			= new t_requires_building( k_town_fort );
	m_citadel		= new t_requires_building( k_town_citadel );
	m_castle		= new t_requires_building( k_town_castle );
	m_caravan		= new t_requires_building( k_town_caravan );
	m_tavern		= new t_requires_building( k_town_tavern );
	m_blacksmith	= new t_requires_building( k_town_blacksmith );
	m_mage_guild_1	= new t_requires_building( k_town_mage_guild_1 );
	m_mage_guild_2	= new t_requires_building( k_town_mage_guild_2 );
	m_mage_guild_3	= new t_requires_building( k_town_mage_guild_3 );
	m_mage_guild_4	= new t_requires_building( k_town_mage_guild_4 );
	m_mage_guild_5	= new t_requires_building( k_town_mage_guild_5 );
	m_library_1		= new t_requires_building( k_town_library_1 );
	m_library_2		= new t_requires_building( k_town_library_2 );
}


void t_initializer::common( t_requirement_ptr requirements[] )
{
	requirements[k_town_village_hall]	= new t_requires_nothing;
	requirements[k_town_hall]			= m_village_hall;
	requirements[k_town_city_hall]		= m_town_hall;
	requirements[k_town_fort]			= m_village_hall;
	requirements[k_town_citadel]		= m_fort;
	requirements[k_town_castle]			= m_citadel;
	requirements[k_town_caravan]		= m_fort;
	requirements[k_town_shipyard]		= m_fort;
	requirements[k_town_tavern]			= m_village_hall;
	requirements[k_town_blacksmith]		= m_village_hall;
	requirements[k_town_dwelling_1]		= m_fort;
	requirements[k_town_dwelling_2]		= m_fort;
}

// -----------------------------------------------------------
// initialize requirements
// -----------------------------------------------------------
void t_initializer::magic_guild( t_requirement_ptr requirements[] )
{
	requirements[k_town_mage_guild_1] = m_village_hall;
	requirements[k_town_mage_guild_2] = m_mage_guild_1;
	requirements[k_town_mage_guild_3] = m_mage_guild_2;
	requirements[k_town_mage_guild_4] = m_mage_guild_3;
	requirements[k_town_mage_guild_5] = m_mage_guild_4;

	requirements[k_town_library_1] = m_mage_guild_1;
	requirements[k_town_library_2] = m_mage_guild_1;
}

// -----------------------------------------------------------
// initialize requirements
// -----------------------------------------------------------
void t_initializer::life()
{
	t_requirement_ptr abbey			= new t_requires_building( k_town_abbey );
	t_requirement_ptr seminary		= new t_requires_building( k_town_seminary );
	t_requirement_ptr stables		= new t_requires_building( k_town_stables );
	t_requirement_ptr squire		= new t_requires_building( k_town_dwelling_squire );
	t_requirement_ptr crossbowman	= new t_requires_building( k_town_dwelling_crossbowman );
	t_requirement_ptr pikeman		= new t_requires_building( k_town_dwelling_pikeman );
	t_requirement_ptr ballista		= new t_requires_building( k_town_dwelling_ballista );
	t_requirement_ptr monk			= new t_requires_building( k_town_dwelling_monk );
	t_requirement_ptr crusader		= new t_requires_building( k_town_dwelling_crusader );
	t_requirement_ptr *requirements = k_requirements[k_town_life];

	common( requirements );
	magic_guild( requirements );
	requirements[k_town_abbey] = m_fort;
	requirements[k_town_seminary] = abbey;
	requirements[k_town_stables] = m_village_hall;
	requirements[k_town_dwelling_pikeman] = m_fort && m_tavern && (squire || crossbowman)
											&& not( k_town_dwelling_ballista );
	requirements[k_town_dwelling_ballista] = m_fort && crossbowman && not( k_town_dwelling_pikeman );

	requirements[k_town_dwelling_monk] = m_fort && abbey && (pikeman || ballista)
									  && not( k_town_dwelling_crusader );
	requirements[k_town_dwelling_crusader] = m_fort && m_blacksmith && (pikeman || ballista)
									  && not( k_town_dwelling_monk );

	requirements[k_town_dwelling_champion] = m_fort && stables && m_blacksmith
									  && (monk || crusader)
									  && not( k_town_dwelling_angel );

	requirements[k_town_dwelling_angel] = m_fort && seminary && (monk || crusader)
									  && not( k_town_dwelling_champion );
	set_legal( k_legal[ k_town_life ], requirements );
}

// -----------------------------------------------------------
// initialize requirements
// -----------------------------------------------------------
void t_initializer::order()
{
	t_requirement_ptr university	= new t_requires_building( k_town_university );
	t_requirement_ptr treasury		= new t_requires_building( k_town_treasury );
	t_requirement_ptr dwarf			= new t_requires_building( k_town_dwelling_dwarf );
	t_requirement_ptr halfling		= new t_requires_building( k_town_dwelling_halfling );
	t_requirement_ptr golem			= new t_requires_building( k_town_dwelling_golem );
	t_requirement_ptr mage			= new t_requires_building( k_town_dwelling_mage );
	t_requirement_ptr genie			= new t_requires_building( k_town_dwelling_genie );
	t_requirement_ptr naga			= new t_requires_building( k_town_dwelling_naga );
	t_requirement_ptr *requirements = k_requirements[k_town_order];

	common( requirements );
	magic_guild( requirements );
	requirements[k_town_university]	= m_fort;
	requirements[k_town_treasury]	= m_town_hall && m_fort;
	requirements[k_town_dwelling_golem]	= m_fort && m_mage_guild_1 && (dwarf || halfling)
		                                  && not( k_town_dwelling_mage );
	requirements[k_town_dwelling_mage]	= m_fort && m_mage_guild_1 && (dwarf || halfling)
		                                  && not( k_town_dwelling_golem );
	requirements[k_town_dwelling_genie]	= m_fort && m_library_1 && (mage || golem)
										  && not( k_town_dwelling_naga );
	requirements[k_town_dwelling_naga]	= m_fort && treasury && (mage || golem)
										  && not( k_town_dwelling_genie );
	requirements[k_town_dwelling_dragon_golem]	= m_fort && m_blacksmith && university
											      && (genie || naga)
											      && not( k_town_dwelling_titan);
	requirements[k_town_dwelling_titan] = m_castle && (genie || naga)
										  && not( k_town_dwelling_dragon_golem );
	set_legal( k_legal[k_town_order], requirements );
};


// -----------------------------------------------------------
// initialize requirements
// -----------------------------------------------------------
void t_initializer::death()
{
	t_requirement_ptr skeleton	= new t_requires_building( k_town_dwelling_skeleton );
	t_requirement_ptr imp		= new t_requires_building( k_town_dwelling_imp );
	t_requirement_ptr ghost		= new t_requires_building( k_town_dwelling_ghost );
	t_requirement_ptr cerberus	= new t_requires_building( k_town_dwelling_cerberus );
	t_requirement_ptr vampire	= new t_requires_building( k_town_dwelling_vampire );
	t_requirement_ptr demon		= new t_requires_building( k_town_dwelling_venom_spawn );
	t_requirement_ptr transformer	= new t_requires_building( k_town_skeleton_transformer );
	t_requirement_ptr amplifier		= new t_requires_building( k_town_necromancy_amplifier );
	t_requirement_ptr *requirements = k_requirements[k_town_death];

	common( requirements );
	magic_guild( requirements );
	requirements[k_town_skeleton_transformer] = skeleton;
	requirements[k_town_necromancy_amplifier] = m_fort && m_mage_guild_1;
	
	requirements[k_town_dwelling_ghost]	= m_fort && transformer && (skeleton || imp) 
										&& not( k_town_dwelling_cerberus );
	requirements[k_town_dwelling_cerberus]	= m_fort && imp && not( k_town_dwelling_ghost );
	requirements[k_town_dwelling_vampire]	= m_fort && m_tavern && (ghost || cerberus) 
											&& not( k_town_dwelling_venom_spawn);
	requirements[k_town_dwelling_venom_spawn]	= m_fort && m_mage_guild_1 && (ghost || cerberus) 
												&& not( k_town_dwelling_vampire );
	requirements[k_town_dwelling_bone_dragon]	= m_fort && amplifier && (vampire || demon)
												&& not( k_town_dwelling_devil );
	requirements[k_town_dwelling_devil]			= m_fort && m_library_2 && (vampire || demon) 
												&& not( k_town_dwelling_bone_dragon );
	set_legal( k_legal[k_town_death], requirements );
}

// -----------------------------------------------------------
// initialize requirements
// -----------------------------------------------------------
void t_initializer::chaos()
{
	t_requirement_ptr bandit	= new t_requires_building( k_town_dwelling_bandit );
	t_requirement_ptr orc		= new t_requires_building( k_town_dwelling_orc );
	t_requirement_ptr medusa	= new t_requires_building( k_town_dwelling_medusa );
	t_requirement_ptr minotaur	= new t_requires_building( k_town_dwelling_minotaur );
	t_requirement_ptr nightmare	= new t_requires_building( k_town_dwelling_nightmare );
	t_requirement_ptr efreet	= new t_requires_building( k_town_dwelling_efreet );
	t_requirement_ptr training_grounds	= new t_requires_building( k_town_training_grounds );

	t_requirement_ptr *requirements = k_requirements[k_town_chaos];

	common( requirements );
	magic_guild( requirements );

	requirements[k_town_academy]	= m_fort;
	requirements[k_town_training_grounds]	= m_fort;
	requirements[k_town_mana_vortex]		= m_mage_guild_2;
	requirements[k_town_dwelling_medusa]	= m_fort && m_mage_guild_1 && (bandit || orc)
											&& not( k_town_dwelling_minotaur );
	requirements[k_town_dwelling_minotaur]	= m_fort && training_grounds && (bandit || orc)
											&& not( k_town_dwelling_medusa );
	requirements[k_town_dwelling_nightmare]	= m_fort && m_caravan && (medusa || minotaur)
											&& not( k_town_dwelling_efreet );
	requirements[k_town_dwelling_efreet]	= m_fort && m_blacksmith && (medusa || minotaur)
											&& not( k_town_dwelling_nightmare );
	requirements[k_town_dwelling_hydra]		= m_fort && m_library_2 && (nightmare || efreet)
											&& not( k_town_dwelling_black_dragon );
	requirements[k_town_dwelling_black_dragon]	= m_fort && m_library_1 && (nightmare || efreet)
												&& not( k_town_dwelling_hydra );
	set_legal( k_legal[k_town_chaos], requirements );
}


// -----------------------------------------------------------
// initialize requirements
// -----------------------------------------------------------
void t_initializer::nature()
{
	t_requirement_ptr wolf	= new t_requires_building( k_town_dwelling_wolf );
	t_requirement_ptr fairie	= new t_requires_building( k_town_dwelling_fairie );
	t_requirement_ptr tiger		= new t_requires_building( k_town_dwelling_tiger );
	t_requirement_ptr elf		= new t_requires_building( k_town_dwelling_elf );
	t_requirement_ptr unicorn	= new t_requires_building( k_town_dwelling_unicorn );
	t_requirement_ptr griffin	= new t_requires_building( k_town_dwelling_griffin );
	t_requirement_ptr grove		= new t_requires_building( k_town_grove );
	t_requirement_ptr rainbow	= new t_requires_building( k_town_rainbow );
	t_requirement_ptr portal	= new t_requires_building( k_town_portal );
	t_requirement_ptr *requirements = k_requirements[k_town_nature];

	common( requirements );
	magic_guild( requirements );

	requirements[k_town_rainbow]	= m_village_hall;
	requirements[k_town_grove]		= m_village_hall;
	
	requirements[k_town_dwelling_tiger]	= m_fort && grove && (wolf || fairie) 
										&& not( k_town_dwelling_elf );
	requirements[k_town_dwelling_elf]	= m_fort && m_blacksmith && (wolf || fairie) 
										&& not( k_town_dwelling_tiger );
	requirements[k_town_dwelling_unicorn]	= m_fort && rainbow && (tiger || elf) 
											&& not( k_town_dwelling_griffin );
	requirements[k_town_dwelling_griffin]	= m_fort && m_citadel && (tiger || elf)
											&& not( k_town_dwelling_unicorn );
	requirements[k_town_portal]	= m_mage_guild_1 && (unicorn || griffin);
	requirements[k_town_dwelling_pheonix]	= m_fort && portal && (unicorn || griffin) 
											&& not( k_town_dwelling_fairie_dragon );
	requirements[k_town_dwelling_fairie_dragon]	= m_fort && m_library_1 && (unicorn || griffin)
											&& not( k_town_dwelling_pheonix );
	set_legal( k_legal[k_town_nature], requirements );
}


// -----------------------------------------------------------
// initialize requirements
// -----------------------------------------------------------
void t_initializer::might()
{
	t_requirement_ptr berserker	= new t_requires_building( k_town_dwelling_berserker );
	t_requirement_ptr centaur	= new t_requires_building( k_town_dwelling_centaur );
	t_requirement_ptr nomad		= new t_requires_building( k_town_dwelling_nomad );
	t_requirement_ptr harpy		= new t_requires_building( k_town_dwelling_harpy );
	t_requirement_ptr ogre		= new t_requires_building( k_town_dwelling_ogre );
	t_requirement_ptr cyclops	= new t_requires_building( k_town_dwelling_cyclops );
	t_requirement_ptr training_grounds	= new t_requires_building( k_town_training_grounds );
	t_requirement_ptr arena				= new t_requires_building( k_town_arena );
	t_requirement_ptr *requirements = k_requirements[k_town_might];

	common( requirements );

	requirements[k_town_breeding_pit]	= ogre || cyclops;
	requirements[k_town_magic_dampener]	= m_fort;
	requirements[k_town_arena]			= m_fort;
	requirements[k_town_training_grounds]	= m_village_hall;
	requirements[k_town_dwelling_nomad]		= m_fort && m_caravan && (berserker || centaur) 
											&& not( k_town_dwelling_harpy );
	requirements[k_town_dwelling_harpy]		= m_fort && m_citadel && (berserker || centaur)
											&& not( k_town_dwelling_nomad );
	requirements[k_town_dwelling_ogre]		= m_fort && m_tavern && (nomad || harpy)
											&& not( k_town_dwelling_cyclops );
	requirements[k_town_dwelling_cyclops]	= m_fort && training_grounds && (nomad || harpy)
											&& not( k_town_dwelling_ogre );
	requirements[k_town_dwelling_thunderbird]	= m_castle && (ogre || cyclops) 
												&& not( k_town_dwelling_behemoth );
	requirements[k_town_dwelling_behemoth]		= m_fort && arena && m_blacksmith && (ogre || cyclops)
												  && not( k_town_dwelling_thunderbird );
	set_legal( k_legal[k_town_might], requirements );
}

// -----------------------------------------------------------
// initialize requirements
// -----------------------------------------------------------
void t_initializer::set_legal( t_town_building_set& legal, t_requirement_ptr requirements[] )
{
	int i;

	legal.reset();
	for (i = 0; i < k_town_building_count; i++)
		if (requirements[i] != 0)
			legal.set(i);
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
t_requirement_ptr t_requires::and( t_requires* arg)
{
	t_requirement_ptr result = new t_and;

	if (dynamic_cast<t_and*>(arg) != 0)
		return arg->and( this );

	result->and( this );
	result->and( arg );
	return result;
}

void t_requires::ai_distribute_value( t_town const* town, float distribute_amount, float* values )
{
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
t_requirement_ptr t_requires_nothing::clone() const
{
	return new t_requires_nothing;
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
bool t_requires_nothing::enabled( t_town const* town ) const
{
	return true;
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
std::string t_requires_nothing::get_text( t_town const* town, bool& use_prefix, t_town_building this_building ) const
{
	return "";
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
bool t_requires_nothing::test( t_town const* town ) const
{
	return true;
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
t_requirement_ptr t_requires_nothing::and( t_requires* arg )
{
	return arg;
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
t_requires_building::t_requires_building( t_town_building building )
{
	m_building = building;
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
t_requirement_ptr t_requires_building::clone() const
{
	return new t_requires_building( m_building );
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
bool t_requires_building::enabled( t_town const* town ) const
{
	return town->is_enabled( m_building, true );
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
void t_requires_building::ai_distribute_value( t_town const* town, float distribute_amount, float* values )
{
	if (!town->has( m_building ))
	{
		values[m_building] += distribute_amount;
		
		t_requirement_ptr requires = k_requirements[town->get_type()][m_building];
		if (requires.get() != NULL)
			requires->ai_distribute_value( town, distribute_amount, values );
	}
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
std::string t_requires_building::get_text( t_town const* town, bool& use_prefix, t_town_building this_building ) const
{
	if (town->has( m_building ))
		return "";

	t_building_traits const& traits = get_traits( town->get_type(), m_building );

	use_prefix = true;
	return traits.name;
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
bool t_requires_building::test( t_town const* town ) const
{
	return town->has( m_building );
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
t_or::t_or( t_requirement_ptr left, t_requirement_ptr right ) : m_left( left ), m_right( right )
{
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
t_requirement_ptr t_or::clone() const
{
	return new t_or( m_left, m_right );
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
static t_external_string const k_text_or( "or.town_build" );
std::string t_or::get_text( t_town const* town, bool& use_prefix, t_town_building this_building ) const
{
	bool dummy;
	std::string left = m_left->get_text( town, dummy, this_building );
	std::string right = m_right->get_text( town, dummy, this_building );

	if (left.empty() || right.empty())
		return "";
	use_prefix = true;
	return left + " " + k_text_or.get() + " " + right;
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
bool t_or::enabled( t_town const* town ) const
{
	return m_left->enabled( town ) || m_right->enabled( town );
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
bool t_or::test( t_town const* town ) const
{
	return m_left->test( town ) || m_right->test( town );
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
void t_or::ai_distribute_value( t_town const* town, float distribute_amount, float* values )
{
	m_left->ai_distribute_value( town, distribute_amount, values );
	m_right->ai_distribute_value( town, distribute_amount, values );
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
t_requirement_ptr t_and::clone() const
{
	return new t_and( *this );
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
t_requirement_ptr t_and::and( t_requires* arg )
{
	t_and* and_arg = dynamic_cast<t_and*>(arg);

	if (and_arg != 0)
	{
		int i;

		for (i = 0; i < and_arg->m_requirements.size(); i++)
			and( and_arg->m_requirements[i] );
		return this;
	}

	m_requirements.push_back( arg );
	return this;
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
void t_and::ai_distribute_value( t_town const* town, float distribute_amount, float* values )
{
	int i;

	for (i = 0; i < m_requirements.size(); i++)
		m_requirements[i]->ai_distribute_value( town, distribute_amount, values );
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
std::string t_and::get_text( t_town const* town, bool& use_prefix, t_town_building this_building ) const
{
	std::string result;
	std::string new_string;
	int         i;

	for (i = 0; i < m_requirements.size(); i++)
	{
		new_string = m_requirements[i]->get_text( town, use_prefix, this_building );
		if (new_string.empty())
			continue;

		if (result.empty())
		{
			result = new_string;
			continue;
		}

		if (dynamic_cast<t_requires_building*>( m_requirements[i].get() ) != 0)
			result += ", " + new_string;
		else
			result += "\n" + new_string;
	}
	return result;
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
bool t_and::enabled( t_town const* town ) const
{
	int i;

	for (i = 0; i < m_requirements.size(); i++)
		if (!m_requirements[i]->enabled( town ))
			return false;
	return true;
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
bool t_and::test( t_town const* town ) const
{
	int i;

	for (i = 0; i < m_requirements.size(); i++)
		if (!m_requirements[i]->test( town ))
			return false;
	return true;
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
t_not::t_not( t_town_building building ) : m_building( building )
{
}

t_requirement_ptr t_not::clone() const
{
	return new t_not( m_building );
}


//static t_external_string const k_text_cannot_build_if( "cannot_build_if.town_build" );
//static t_external_string const k_text_is_built( "is_built.town_build" );
static t_external_string const k_text_prevent_building( "prevent_building.town_build" );
std::string t_not::get_text( t_town const* town, bool& use_prefix, t_town_building this_building ) const
{
	t_building_traits const& traits = get_traits( town->get_type(), m_building );
	t_building_traits const& this_traits = get_traits( town->get_type(), this_building );

	return replace_keywords( k_text_prevent_building, "%this_building", this_traits.name, 
													"%other_building", traits.name ); 
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
bool t_not::enabled( t_town const* town ) const
{
	return !town->has( m_building );
}

// -----------------------------------------------------------
// prerequisite logic
// -----------------------------------------------------------
bool t_not::test( t_town const* town ) const
{
	return !town->has( m_building );
}


// -----------------------------------------------------------
// initialize requirements
// -----------------------------------------------------------
static void init_requirements()
{
	static bool initialized = false;

	if (initialized)
		return;

	t_initializer init;

	initialized = true;

	init.life();
	init.order();
	init.death();
	init.chaos();
	init.nature();
	init.might();
}

// -----------------------------------------------------------
// -----------------------------------------------------------
void ai_distribute_value_to_requirements( t_town* town, t_town_building building, float const* raw_values, float* values )
{
	if (!town->is_legal( building ) || town->has( building )) 
		return;

	t_requirement_ptr requires = k_requirements[town->get_type()][building];

	if (requires != 0)
		requires->ai_distribute_value( town, raw_values[building], values );
}

// -----------------------------------------------------------
// check if a given building is legal for a town
// -----------------------------------------------------------
bool is_legal( t_town_type town, t_town_building building )
{
	init_requirements();

	return k_legal[town][building];
}

// -----------------------------------------------------------
// Temporary data, read in by read_from_map and used by 
// initialize
// -----------------------------------------------------------
struct t_town::t_temp_read_from_map_data
{
	t_spell_set	m_required_spells;
	t_spell_set m_potential_spells;
	t_skill_set m_available_skills;
	t_skill_set m_required_skills;
};


// -----------------------------------------------------------
// town data 
// -----------------------------------------------------------
static t_creature_type const k_default_portal_creature = k_waspwort;

t_town::t_town() : t_abstract_town( "castle-towns.order fort" )
{
	init();

	m_type = t_town_type( get_major_subtype() );

	m_enabled_buildings.set();
}


// -----------------------------------------------------------
// town data 
// -----------------------------------------------------------
t_town::t_town( std::string const& source, t_qualified_adv_object_type const& type )
      : t_abstract_town( source )
{
	init();

	m_type = t_town_type( type.major_subtype() );
}

// -----------------------------------------------------------
// town data 
// -----------------------------------------------------------
t_town::t_town( t_abstract_town* source )
      : t_abstract_town( source )
{
	init();
}

void t_town::init()
{
	m_terrain = k_terrain_snow;
	m_portal_creature = k_default_portal_creature;
	m_portal_charge = 0;
	m_tavern_delay = 0;

	memset( m_population, 0, sizeof( m_population ));
	m_buildings.set( k_town_village_hall );
	m_buildings.set( k_town_prison );

	m_single_use_id = -1;
	m_training_grounds_use_id = -1;

	m_ai_town_data_cache_ptr.reset( new t_ai_town_data_cache );

	m_last_treasury_bonus = 0;
	m_mana_vortex_delay = 0;

	m_building_delay = 0;
}


// -----------------------------------------------------------
// town data 
// -----------------------------------------------------------
t_town::~t_town()
{
}


// -----------------------------------------------------------
// check if town owner can afford a building
// -----------------------------------------------------------
bool t_town::can_buy( t_town_building building ) const
{
	if ((get_owner_number() < 0) || has_built())
		return false;

	t_player const& player = *get_owner();
	
	t_building_traits const& traits = get_traits( m_type, building );

	if (player.get_funds() < traits.cost)
		return false;

	return can_build( building );
}

// -----------------------------------------------------------
// check if town owner can afford a building - no other check.
// -----------------------------------------------------------
bool t_town::can_afford( t_town_building building ) const
{
	if (get_owner_number() < 0)
		return false;

	t_player const& player = *get_owner();
	
	t_building_traits const& traits = get_traits( m_type, building );

	if (player.get_funds() < traits.cost)
		return false;

	return true;
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
bool t_town::can_build( t_town_building building ) const
{
	if (!is_legal( building ) || m_buildings[building]) 
		return false;

	t_requirement_ptr requires = k_requirements[m_type][building];

	if (requires == 0)
		return true;
	return requires->test( this );
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
void t_town::buy_building( t_town_building building )
{
	if (get_owner_number() >= 0)
	{
		apply_building_delay();
			
		t_building_traits const& traits = get_traits( m_type, building );

		get_owner()->spend( traits.cost );
	}
	create_building( building );
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
bool t_town::has_built() const
{
	return (m_building_delay > 0);
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
static int const k_portal_daily_charge = 100;

void t_town::create_building( t_town_building building )
{
	m_buildings.set( building );
	get_towndwelling_count();		// assert if count is bad
	update_model();
	update_spells();
	learn_spells( this );

	switch (building)
	{
		case k_town_grove:
		case k_town_arena:
		case k_town_academy:
			if (m_map != 0 )
			{
				assert(m_single_use_id == -1);
				m_single_use_id = m_map->get_new_single_use_id();
			}
			break;
		case k_town_training_grounds:
			if (m_map != 0)
			{
				assert(m_training_grounds_use_id == -1);
				m_training_grounds_use_id = m_map->get_new_single_use_id();
			}
			break;
		case k_town_portal:
			m_portal_charge = k_portal_daily_charge * 7 / 2;
			m_portal_creature = k_default_portal_creature;
			break;
	}

	int dwelling_number = building - k_town_dwelling_1;

	if (dwelling_number < 0 || dwelling_number >= k_dwelling_count)
		return;

	// add initial population
	t_building_traits const& building_traits = get_traits( m_type, building );

	if (building_traits.creature == k_creature_none)
		return;

	t_creature_traits const& creature_traits = get_traits( building_traits.creature );

	m_population[dwelling_number] = ( creature_traits.weekly_growth + 1 ) * k_population_divisor / 2;
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
int t_town::get_population( t_creature_type creature )
{
	t_creature_traits const& traits = get_traits( creature );

	if (traits.alignment != m_type)
		return 0;

	t_town_building building = traits.dwelling;
	int             dwelling = building - k_town_dwelling_1;

	assert( dwelling >= 0 && dwelling < k_dwelling_count );
	return m_population[dwelling] / k_population_divisor;
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
int t_town::get_population( int dwelling ) const
{
	assert((dwelling >= 0) && (dwelling < k_dwelling_count));
	
	return m_population[dwelling] / k_population_divisor;
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
bool t_town::adjust_population(int dwelling, int amount)
{
	if (dwelling >= k_dwelling_count) return false;

	m_population[dwelling] += ( amount * k_population_divisor );
	
	if (m_population[dwelling] < 0) m_population[dwelling] = 0;

	return true;
}

// -----------------------------------------------------------
// hire creatures
// -----------------------------------------------------------
int t_town::hire_creatures( t_creature_type creature, int amount )
{
	t_creature_traits const& traits = get_traits( creature );
	t_town_building			 building = traits.dwelling;
	int                      population;
	int						 dwelling = building - k_town_dwelling_1;

	if (traits.alignment != m_type)
		return 0;

	assert( dwelling >= 0 && dwelling < k_dwelling_count );

	// limit to # of creatures in town
	population = m_population[dwelling] / k_population_divisor;
	if (population < amount)
		amount = population;

	amount = create_creatures( creature, amount );
	m_population[dwelling] -= amount * k_population_divisor;

	return amount;
}

// -----------------------------------------------------------
// hire creatures
// -----------------------------------------------------------
int	t_town::create_creatures( t_creature_type creature, int amount )
{
	t_creature_traits const& traits = get_traits( creature );
	t_player&                player = *get_owner();
	int                      population;

	if (!can_add( creature ))
		return 0;

	// limit to amount player can afford
	population = player.get_funds() / traits.cost;
	if (population < amount)
		amount = population;

	if (amount > 0)
	{
		t_material_array cost  = traits.cost * amount;
		t_creature*		 stack = new t_creature( creature, amount );

		player.spend( cost );
		stack->process_new_day( get_owner(), false, NULL, 0 );
		add( stack );
	}

	assert( amount >= 0 );
	return amount;
}

// -----------------------------------------------------------
// use portal
// -----------------------------------------------------------
int t_town::use_portal( int amount )
{
	t_creature_traits const& traits = get_traits( m_portal_creature );
	int                      population;

	// limit to charge of portal
	population = m_portal_charge / traits.experience;
	if (population < amount)
		amount = population;

	amount = create_creatures( m_portal_creature, amount );
	m_portal_charge -= amount * traits.experience;

	assert( amount >= 0 );
	return amount;
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
static t_external_string const k_text_all_requirements_met( "all_requirements_met.town_build" );
static t_external_string const k_text_requires( "requires.town_build" );
std::string t_town::get_requirements_text( t_town_building building ) const
{
	std::string requirements;
	bool        use_prefix = false;

	if (k_requirements[m_type][building] != 0)
	{
		requirements = k_requirements[m_type][building]->get_text( this, use_prefix, building );
	}

	if (requirements.empty())
	{
		requirements = k_text_all_requirements_met.get();
	}
	else if (use_prefix)
		requirements = k_text_requires.get() + " " + requirements;
	return requirements;
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
bool t_town::is_enabled( t_town_building building, bool check_requirements ) const
{
	if (m_buildings[building])
		return true;
	if (!m_enabled_buildings[building])
		return false;
	if (!check_requirements)
		return true;
	if (k_requirements[m_type][building] == 0)
		return true;
	return k_requirements[m_type][building]->enabled( this );
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
void t_town::set_terrain()
{
	t_level_map_point_2d					  point = get_position();
	t_model const&			      model = get_model();
	t_map_point_2d                size = model.get_size();

	// look for first cell with trigger edge
	t_map_point_2d trigger_cell;
	bool           found = false;

	for (trigger_cell.row = 0; trigger_cell.row <= size.row && !found; trigger_cell.row++)
	{
		for (trigger_cell.column = 0; trigger_cell.column <= size.column; trigger_cell.column++)
		{
			if ((trigger_cell.row < size.row
				 && model.is_left_edge_trigger( trigger_cell ))
				|| (trigger_cell.column < size.column 
				    && model.is_right_edge_trigger( trigger_cell )))
			{
				found = true;
				break;
			}
		}
	}

	if (found)
		point += trigger_cell;

	t_abstract_adventure_map::t_tile const* tile = &m_map->get_tile( point );

	m_terrain = tile->get_terrain();
	if (m_terrain >= k_terrain_grass && m_terrain <= k_terrain_subterranean)
		return;

	// we prefer grass -> dirt for the backdrop.  Let's look for nearby cells to find one.
	t_map_point_2d cell;
	t_level_map_point_2d    origin = get_position();
	t_level_map_point_2d    new_point;
	int            distance;
	int            closest = 1000;
	
	for (cell.row = 0; cell.row < size.row; cell.row++)
	{
		for (cell.column = 0; cell.column < size.column; cell.column++)
		{
			new_point = origin + cell;
			if (!m_map->is_valid( new_point ))
				continue;
			tile = &m_map->get_tile( new_point );
			if (tile->get_terrain() < k_terrain_grass || tile->get_terrain() > k_terrain_subterranean)
				continue;
			distance =  (point.row - new_point.row) * (point.row - new_point.row)
				      + (point.column - new_point.column) * (point.column - new_point.column);
			if (distance > closest)
				continue;
			m_terrain = tile->get_terrain();
			closest = distance;
		}
	}
}

// -------------------------------------------------------------------
// class to represent adventure objects (from trees to castles)
// -------------------------------------------------------------------
void t_town::left_double_click( t_mouse_event const& event, 
							    t_adventure_frame* adventure_frame )
{
	assert( adventure_frame != 0 );
	if ( same_team( &m_map->get_player(), get_owner() ) )
		adventure_frame->do_town_dialog( this, 0 );
}

// ---------------------------------------------------------------
// army which appears on the adventure map
// get the correct cursor
// ---------------------------------------------------------------
t_bitmap_cursor const& t_town::get_cursor( t_adventure_map_window const& window,
		                                   t_army const* army ) const
{
	if (army == 0)
		return window.get_normal_cursor();
	if ( same_team( army->get_owner(), get_owner() ) )
		return window.get_activate_cursor();
	return window.get_attack_cursor();
}

// ----------------------------------------------------------------------
// Handle a visit from the current set of armies to a single-use building
// ----------------------------------------------------------------------
void t_town::visit_single_use( bool check_garrison, t_army *adjacent_army, int single_use_id, t_town_building building, t_window* frame )
{
	// Note: largely copied from t_single_use_object::activate_trigger

	int                  array, creature;
	t_hero*              hero;
	std::vector<t_hero*> heroes;
	bool				 any_benefit = false;
	bool				 human_benefit = false;

	t_creature_array    *creature_arrays[2];

	if ( check_garrison )
	{
		creature_arrays[0] = this;
	}
	else
	{
		creature_arrays[0] = NULL;
	}

	if (adjacent_army)
	{
		creature_arrays[1] = &adjacent_army->get_creatures();
	}
	else
	{
		creature_arrays[1] = NULL;
	}

	for (array = 0; array < ELEMENTS_OF(creature_arrays); array++)
	{
		if (creature_arrays[array])
		{
			t_player* owner = creature_arrays[array]->get_owner();

			// If we are going to need to display a message, and don't have a window, don't give out
			// bonuses. If we are going to need to display a message, and the wrong person is
			// looking at the screen, don't give out bonuses
			if (   owner 
				&& !owner->is_computer() 
				&& (!frame || owner != &get_map()->get_player() ) )
			{
				continue;
			}

			for (creature = 0; creature < t_creature_array::k_size; creature++)
			{
				hero = (*creature_arrays[array])[creature].get_hero();
				if (hero == 0)
					continue;

				if (hero->is_dead())
					continue;

				if (hero->has_visited( single_use_id ))
					continue;

				t_player* owner = creature_arrays[array]->get_owner();
				if ( (owner != NULL) && !owner->is_computer() )
				{
					human_benefit = true;
				}

				any_benefit = true;

				switch (building)
				{
					case k_town_grove:
						add_to_spell_points( hero, 3 );
						break;

					case k_town_arena:
						hero->add_bonus( k_stat_defense, 3 );
						break;

					case k_town_academy:
						hero->add_experience( 1000 );
						break;

					case k_town_training_grounds:
						hero->add_bonus( k_stat_attack, 3 );
						break;

					default:
						assert(false);
						return;
				}

				heroes.push_back( hero );
				hero->set_visited( single_use_id );
			}
		}
	}

	if ( human_benefit )
	{
		t_counted_ptr<t_basic_dialog> dialog;

		// initialize dialog
		dialog = new t_basic_dialog(frame);

		std::string text;
		std::string keyword;
		const char *replace_string;
		const char *with_string;


		t_sound_cache music = get_music_playing();
		stop_music();

		// Add icon for benefit type
		switch(building)
		{
			case k_town_grove:
				get_dialog_sound( k_dialog_sound_mana )->play( get_sound_volume() );
				dialog->add_spell_points( 3 );
				keyword = "sacred_grove.";
				replace_string = "%spell_points";
				with_string = "3";
				break;

			case k_town_arena:
				get_dialog_sound( k_dialog_sound_military )->play( get_sound_volume() );
				dialog->add_defense( 3 );
				keyword = "arena_of_honor.";
				replace_string = "%defense";
				with_string = "3";
				break;

			case k_town_academy:
				get_dialog_sound( k_dialog_sound_experience )->play( get_sound_volume() );
				dialog->add_experience( 1000 );
				keyword = "battle_academy.";
				replace_string = "%experience";
				with_string = "1000";
				break;

			case k_town_training_grounds:
				get_dialog_sound( k_dialog_sound_military )->play( get_sound_volume() );
				dialog->add_attack( 3 );
				keyword = "training_grounds.";
				replace_string = "%attack";
				with_string = "3";
				break;
		}


		if (heroes.size() == 1)
		{
			keyword += "one_hero";
			text = replace_keywords( get_text( keyword ), heroes.front() );
		}
		else
		{
			keyword += "several_heroes";
			text = replace_keywords( get_text( keyword ), heroes );
		}

		text = replace_keywords( text, replace_string, with_string);

		dialog->set_text( text );
		dialog->add_ok_button();

		t_building_traits const& traits = get_traits( m_type, building );
		dialog->set_title( traits.name );
		
		dialog->open();
		dialog->run_modal();
		play_music( music );
	}

	if (any_benefit && k_town_academy == building)
	{
		for (int i = 0; i < heroes.size(); i++)
		{
			heroes[i]->check_level( *get_map(), frame, get_owner() );
		}
	}
}

// ----------------------------------------------------------------------
// Handles luck and morale boosting buildings
// ----------------------------------------------------------------------
void t_town::visit_booster( bool check_garrison, t_army *adjacent_army, t_town_building building, t_window* frame )
{
	int  array, creature;
	bool display_message = false;

	t_stat_type stat_type;
	t_qualified_adv_object_type object_type;
	int amount;
	
	if (building == k_town_rainbow)
	{
		stat_type = k_stat_luck;
		object_type = t_qualified_adv_object_type(k_adv_object_rainbow);
		amount = 2;
	}
	else if ( building == k_town_stables ) 
	{
		stat_type = k_stat_movement;
		object_type =  t_qualified_adv_object_type(k_adv_object_movement_booster, k_movement_stables);
		amount = 500; 
	}
	else
	{
		assert(building == k_town_abbey);
		stat_type = k_stat_morale;
		object_type = t_qualified_adv_object_type(k_adv_object_abbey);
		amount = 2;
	}

	// loop through all heroes/creatures in the army and award temporary statistic based on building type
	t_creature_array    *creature_arrays[2];

	if ( check_garrison )
	{
		creature_arrays[0] = this;
	}
	else
	{
		creature_arrays[0] = NULL;
	}

	if (adjacent_army)
	{
		creature_arrays[1] = &adjacent_army->get_creatures();
	}
	else
	{
		creature_arrays[1] = NULL;
	}

	for (array = 0; array < ELEMENTS_OF(creature_arrays); array++)
	{
		if (creature_arrays[array])
		{
			t_player* owner = creature_arrays[array]->get_owner();

			bool display_this_message = false;

			// If we are going to need to display a message, and don't have a window, don't give out
			// bonuses. If we are going to need to display a message, and the wrong person is
			// looking at the screen, don't give out bonuses
			if ( owner && !owner->is_computer() )
			{
				// Has player already seen this message in this town?
				if ( !owner->seen_town_booster( building ) )
				{
					if ( !frame || owner != &get_map()->get_player() )
					{
						// Need to display message and cannot
						continue;
					}
					else
					{
						display_this_message = true;
					}
				}
			}

			if ( building == k_town_stables )
			{
				const int days = 7;
			
				// Movement bonuses are handles slightly differently than
				// other temp bonuses
				bool result = creature_arrays[array]->add_move_bonus( amount, object_type, days );

				if ( result && display_this_message )
					display_message = true;
			}
			else
			{
				for (creature = 0; creature < t_creature_array::k_size; creature++)
				{
					if ((*creature_arrays[array])[creature].get_number() == 0)
						continue;
					if ((*creature_arrays[array])[creature].has_temporary_bonus( object_type ))
						continue;
					(*creature_arrays[array])[creature].add_bonus( stat_type, amount, object_type );

					if ( display_this_message )
						display_message = true;
				}
			}
		}
	}

	// determine what response to print
	if ( display_message )
	{
		t_counted_ptr<t_basic_dialog>   dialog;
		std::string						text;

		dialog = new t_basic_dialog(frame);
		text = get_string(object_type, "initial" );
		if (stat_type == k_stat_luck)
		{
			dialog->add_luck( amount );
			get_dialog_sound( k_dialog_sound_luck )->play( get_sound_volume() );
		}
		else if ( stat_type == k_stat_movement )
		{
			dialog->add_movement( amount / 100 );
			get_dialog_sound( k_dialog_sound_stables )->play( get_sound_volume() );
		}
		else
		{
			dialog->add_morale( amount );
			get_dialog_sound( k_dialog_sound_morale )->play( get_sound_volume() );
		}

		dialog->set_text( text );
		dialog->add_ok_button();

		t_building_traits const& traits = get_traits( m_type, building );
		dialog->set_title( traits.name );
		
		dialog->open();
		dialog->run_modal();

		get_map()->get_player().set_seen_town_booster( building );
	}
}

// ----------------------------------------------------------------------
// gain spell points for touching a mage guild
// ----------------------------------------------------------------------
static void gain_spell_points( t_creature_array& army, t_town const& town )
{
	int				level = 0;
	t_town_building building;
	
	for (building = k_town_mage_guild_1; building <= k_town_mage_guild_5; enum_incr(building))
	{
		if (town.has( building ))
			level = building - k_town_mage_guild_1 + 1;
	}
	if (level < 1)
		return;

	static int const	k_recover_value[] = { 0, 35, 50, 65, 80, 100 };
	int					i;
	int					new_points;
	t_hero*				hero;

	for (i = 0; i < t_creature_array::k_size; ++i)
	{
		hero = army[i].get_hero();
		if (hero == 0)
			continue;
		
		new_points = hero->get_maximum_spell_points() * k_recover_value[level] / 100;
		if (new_points > hero->get_spell_points())
			hero->set_spell_points( new_points );
	}
}


// ----------------------------------------------------------------------
// Check all armies against the bonus buildings 
// (stables, training grounds, etc)
// Checks the garrison if check_garrison is true . Checks the adjacent_army too, if not NULL
// ----------------------------------------------------------------------
extern t_external_string const k_text_resurrect_heroes( "town_resurrect_heroes.misc" );

void t_town::check_bonus_buildings(t_army *adjacent_army, t_window *frame, bool check_garrison )
{
	// Resurrect, etc adjacent army
	if (adjacent_army != 0)
	{
		resurrect_heroes( *adjacent_army, k_text_resurrect_heroes, frame );
		adjacent_army->learn_spells( this );
		gain_spell_points( *adjacent_army, *this );
	}

	if ( check_garrison )
	{
		// Resurrect, etc garrison
		resurrect_heroes( *this, k_text_resurrect_heroes, frame );
		learn_spells( this );
		gain_spell_points( *this, *this );
	}

	if (has(k_town_grove))
	{
		assert(m_single_use_id != -1);
		visit_single_use( check_garrison, adjacent_army, m_single_use_id, k_town_grove, frame );
	}
	else if (has(k_town_arena))
	{
		assert(m_single_use_id != -1);
		visit_single_use( check_garrison, adjacent_army, m_single_use_id, k_town_arena, frame );
	}
	else if (has(k_town_academy))
	{
		assert(m_single_use_id != -1);
		visit_single_use( check_garrison, adjacent_army, m_single_use_id, k_town_academy, frame );
	}

	if (has(k_town_training_grounds))
	{
		assert(m_training_grounds_use_id != -1);
		visit_single_use( check_garrison, adjacent_army, m_training_grounds_use_id, k_town_training_grounds, 
			              frame );
	}

	if (has(k_town_stables))
	{
		visit_booster( check_garrison, adjacent_army, k_town_stables, frame );
	}

	if (has(k_town_abbey))
	{
		visit_booster( check_garrison, adjacent_army, k_town_abbey, frame );
	}

	if (has(k_town_rainbow))
	{
		visit_booster( check_garrison, adjacent_army, k_town_rainbow, frame );
	}

	// In some cases, we can give a little more
	if ( adjacent_army )
		gain_spell_points( *adjacent_army, *this );

	if ( check_garrison )
		gain_spell_points( *this, *this );
}

// -----------------------------------------------------------
// town data
// An army has attacked the town
// -----------------------------------------------------------
void t_town::army_attacks_town( t_army* army, t_adv_map_point const& point, t_adv_map_point const& source_point,
								t_adventure_frame* adventure_frame, t_combat_result & combat_result )
{
	// force any garrisoned heroes to be alive - should never happen, but some savegames 
	// have old behavior that created this situation.
	resurrect_heroes( *this, "", 0 );
	// Execute the "Attacked" town event.  This event is meant to 
	// occur regardless of if the town is protected by an army.  
	execute_script(k_town_scriptable_begin_combat, army);

	// create army
	if (!empty())
	{
		t_combat_context_ptr context;

		context = new t_combat_context_adv_object( army, this, point, this, 0 );
		run_combat( *context );
		return;
	} 
	captured( army, adventure_frame );

}

void t_town::on_combat_end( t_army* army, t_combat_result combat_result, t_creature_array* losses,
							t_adventure_frame* frame )
{
	if (attacker_lost( combat_result )) 
	{
		execute_script(k_town_scriptable_win_combat, army);
		return;
	} 
	captured( army, frame );
}

static t_external_string const k_text_lost_town( "town_lost_town.misc" );
static t_external_string const k_text_captured_town( "town_captured_town.misc" );

void t_town::captured( t_army* army, t_adventure_frame* adventure_frame )
{
	t_player*	player = get_owner();
	std::string text;
	t_adventure_map_ptr map = get_map();

	if (army->get_living_hero_count() == 0)
	{
		if (army->is_human())
		{
			ok_dialog( replace_keywords( get_text( "no_heroes" ), "%town_name", get_name()), true );
		}
		return;
	}
	if (player != 0 && !player->is_computer())
	{
		text = replace_keywords( k_text_lost_town, "%town_name", get_name() );
		ok_dialog( text, true );
	}
	player = army->get_owner();
	if (player != 0 && !player->is_computer())
	{
		text = replace_keywords( k_text_captured_town, "%town_name", get_name() );
		ok_dialog( text, true );
	}

	set_owner( army->get_owner_number() );

	execute_script(k_town_scriptable_lose_combat, army);

	if ( !map->is_game_over() )
		free_prisoners ();

	if ( !map->is_game_over() )
		kill_arrived_caravans( army );

	if ( !map->is_game_over() )
		warn_of_approaching_caravans();
	
	if ( !map->is_game_over() )
		adventure_frame->update_army(army);
	
	if ( !map->is_game_over() )
		adventure_frame->evaluate_victory_condition();
}

// -----------------------------------------------------------
// Army visits a friendly town
// -----------------------------------------------------------
void t_town::army_visits_town( t_army * army, t_adventure_frame* adventure_frame, bool skip_town_screen )
{
	army->visit_town( this );

	execute_script(k_town_scriptable_entered, army);
	if( get_map()->is_game_over() )
		return;

	// If a neutral army attacked (or a computer player visits), skip the town dialog.
	// If this is a caravan which just won a forced combat, army is a fake and will be
	// going away soon, so skip town dialog
	if (skip_town_screen || army->get_owner() == NULL)
		return;

	if (army->get_owner()->is_computer())
	{
		t_adventure_ai& ai = army->get_map()->get_ai();

		check_bonus_buildings( army, adventure_frame );
		ai.visit_town( *this, *army );
		return;
	}

	adventure_frame->do_town_dialog( this, army );
}

bool t_town::is_triggered_by( t_creature_array const& army ) const
{
	return !army.is_boat();
}

// -----------------------------------------------------------
// town data
// army entering town
// -----------------------------------------------------------
void t_town::activate_trigger( t_army* army, t_adv_map_point const& point, t_direction direction,
							   t_adventure_frame* adventure_frame )
{
	if ( !same_team(army->get_owner(), get_owner()))
	{
		t_adv_map_point new_point = point + get_direction_offset( direction );
		t_combat_result combat_result;
		army_attacks_town( army, point, new_point, adventure_frame, combat_result );
	}
	else
	{
		army_visits_town( army, adventure_frame );
	}
}

// Create army.
bool create_nearby_army_with_hero( t_army_array* armies, t_adventure_map* map_ptr, t_town* town_ptr, int owner, t_hero* hero_ptr )
{
    t_adv_map_point	point;
    
    t_army *  temp_army_ptr = new t_army;
    temp_army_ptr->add ( hero_ptr );
    temp_army_ptr->set_owner( owner );
	        
    if ( !town_ptr->find_nearby_space( point, *temp_army_ptr ) )
		return false;

    temp_army_ptr->place( *map_ptr, point );
    armies->push_back( temp_army_ptr );

	return true;
}

static t_external_string const k_free_prisoner_text( "town_free_prisoner.misc" );
static t_external_string const k_unfreed_prisoner_text( "town_unfreed_prisoner.misc" );
static t_external_string const k_unfreed_prisoner_separator( "town_unfreed_prisoner_separator.misc" );

// Free prisoners, if any.
void t_town::free_prisoners()
{
    int index;
    int j;

	if (m_prisoner_list.size() == 0)
        return;
	if (get_owner_number() < 0)
		return;

    t_player &					player = get_map()->get_player( get_owner_number() );
    const int					team_number = player.get_team();
    bool						any_freed_prisoners = false;
    bool						any_unfreed_prisoners = false;
    t_adventure_map*			map_ptr = get_map();
    t_adventure_frame*			frame_ptr = get_adventure_frame();
    t_army*						army_ptr;
    t_hero_ptr					hero_ptr;
	t_hero_keyword_replacer		replacer;
	t_hero_keyword_replacer		unfreed_replacer;
	int							i;
    
	t_owned_ptr<t_army_array>	armies;
	armies.reset(get_adjacent_armies());

	int							initial_armies_size = armies->size();

    // Check all prisoners.
	index = m_prisoner_list.size();
	while (index--)
    {
        int prisoner_owner = m_prisoner_list[ index ].owner_number;
        t_player & prisoner_player = get_map()->get_player( prisoner_owner );
        int prisoner_team_number = prisoner_player.get_team();
		bool freed;

        // If they're not on our team, let 'em rot!
		if (team_number != prisoner_team_number)
			continue;

		// If their original owner (player) has been eliminated, they belong to us now
		if (prisoner_player.is_eliminated())
			prisoner_owner = get_owner_number();

        hero_ptr = m_prisoner_list[ index ].hero;

        // Can we add hero to this garrison?
        if ( get_owner_number() == prisoner_owner && can_add_hero() )
        {
			add ( hero_ptr );
            freed = true;
        }
        else if ( armies->size() )
        {
	        // Can we add hero to an adjacent army?
            bool added_to_adjacent_army = false;

            // Go through armies.  Can we add to any of these armies?
            for (j=0; j < armies->size(); j++)
            {
            	army_ptr = (*armies)[j];
    
                if ( prisoner_owner != army_ptr->get_owner_number() )
                    continue;

                if ( army_ptr->can_add_hero() )
                {
                    army_ptr->add( hero_ptr );
                    added_to_adjacent_army = true;
                    break;
                }
            }
           
			freed = added_to_adjacent_army;
        }
		else
		{
			freed = false;
		}


		if ( !freed )
		{
			if ( create_nearby_army_with_hero( armies.get(), map_ptr, this, prisoner_owner, hero_ptr ) )
			{
				freed = true;
			}
		}

		if ( freed )
		{
			replacer.add_hero( hero_ptr );

			hero_ptr->set_imprisoned( false );
			m_prisoner_list.erase ( m_prisoner_list.begin() + index );
			any_freed_prisoners = true;
		}
		else
		{
			unfreed_replacer.add_hero( hero_ptr );
			any_unfreed_prisoners = true;
		}
	}
   
    // Show dialog.
    if ( ( any_freed_prisoners || any_unfreed_prisoners ) && !player.is_computer() )
    {
		t_counted_ptr<t_basic_dialog>   result_dialog_ptr;
		std::string                     result_text;
        
		if ( any_freed_prisoners )
		{
			replacer.add_keyword( "%town_name", get_name() );
		    result_text = replacer( k_free_prisoner_text );
		}

		if ( any_unfreed_prisoners )
		{
			if ( !result_text.empty() )
				result_text += k_unfreed_prisoner_separator;

			unfreed_replacer.add_keyword( "%town_name", get_name() );
		    result_text += unfreed_replacer( k_unfreed_prisoner_text );
		}
    
        result_dialog_ptr = new t_basic_dialog;
        result_dialog_ptr->set_text( result_text );
        result_dialog_ptr->add_ok_button();
        result_dialog_ptr->open();
		t_sound_cache music = get_music_playing();
		stop_music();
		get_dialog_sound( k_dialog_sound_thieves_guild )->play( get_sound_volume() );
        result_dialog_ptr->run_modal();
		play_music( music );
    }


	// Trigger events for new armies
	for ( i = initial_armies_size; i < armies->size(); i++ )
	{
		(*armies)[i]->trigger_events();

		if ( m_map->is_game_over() )
			return;
	}
}

// Find adjacent armies.
t_army_array* t_town::get_adjacent_armies()
{
    t_adventure_map*        map_ptr = get_map();
    t_level_map_point_2d	origin = m_map->get_object_pos( m_map_id );
	t_level_map_point_2d    map_point;
    t_map_point_2d			footprint_size = get_footprint().get_size();
    t_map_point_2d			offset;
	t_direction             direction;
    t_map_point_2d          direction_offset;
    t_army_array*           armies = new t_army_array;

    for (offset.row = 0; offset.row < footprint_size.row; offset.row++)
   	{
   		for (offset.column = 0; offset.column < footprint_size.column; offset.column++)
   		{
			for (direction = t_direction(0); direction < k_direction_count; enum_incr( direction ))
            {
				direction_offset = get_direction_offset( direction );
   				map_point = origin + offset + direction_offset;
		
                if ( !map_ptr->is_valid( map_point ) )
       				continue;
                    
                t_adventure_tile const& map_tile = map_ptr->get_adv_tile( t_level_map_point_2d( map_point, origin.level ) );

                // Get number of objects intersecting tile.
				int num_object_count = map_tile.get_intersecting_object_count();

                // Any intersecting objects?
                while (num_object_count)
                {
                    num_object_count--;
	                t_adventure_map_object_id obj_id = map_tile.get_intersecting_object_id( num_object_count );

	                t_adventure_object &object = map_ptr->get_adv_object( obj_id );
                    t_army * army_ptr = dynamic_cast<t_army *> (&object);

                    // Check if it's an army.  If so, add it.
                    if ( army_ptr )
                    {
                        armies->push_back( army_ptr );
                    }
                }
            }
		}
    }

    return ( armies );
}

// -----------------------------------------------------------
// town data
// get weekly growth * k_population_divisor
// -----------------------------------------------------------
int t_town::get_growth( t_town_building building ) const
{
	t_creature_type creature;
	int				growth;
	int				bonus;
	t_player const *owner = get_owner();

	if (building == k_town_portal)
	{
		growth = ( k_portal_daily_charge * 7 * k_population_divisor ) / get_traits( m_portal_creature ).experience;

		return growth;
	}

	creature = get_traits( m_type, building ).creature;
	growth = get_traits( creature ).weekly_growth * k_population_divisor;
	bonus = get_nobility_bonus( );

	if (has( k_town_breeding_pit ))
		bonus += 50;
	
	if ( has( k_town_grail ) && m_type == k_town_might )
	{
		bonus += 100;
	}
	else if ( gets_global_grail_effects( k_town_might ) )
	{
		bonus += 25;
	}

	growth = growth * (100 + bonus) / 100;
	return growth;
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
int t_town::get_growth( t_creature_type creature ) const
{
	t_creature_traits const& traits = get_traits( creature );

	if (traits.alignment != m_type)
		return 0;

	t_town_building building = traits.dwelling;
	int             dwelling = building - k_town_dwelling_1;

	assert( dwelling >= 0 && dwelling < k_dwelling_count );
	if (!has( building ))
		return 0;
	return get_growth( building );
}

// -----------------------------------------------------------
// Should this number be displayed with a decimal point (#.#)?
// No if the number is too big or the tenths place will display
// a zero
// -----------------------------------------------------------
static bool number_needs_decimal(int growth)
{
	int	const max_decimal = 5 * t_town::k_population_divisor; // If growth numbers are above this, that number never needs decimal

	if (growth > max_decimal)
		return false;

	int mod = growth % t_town::k_population_divisor;

	int const min_for_round = 35;
	int const max_for_round = 665;

	// printf will round to nearest tenth
	return (mod >= min_for_round && mod <= max_for_round);
}

// -----------------------------------------------------------
// Displayes a simple dialog explaining where all the growth
// for a creature comes from
// -----------------------------------------------------------
static t_external_string const k_text_weekly_growth_title( "town_weekly_growth_title.misc" );
static t_external_string const k_text_base_growth( "town_base_growth.misc" );
static t_external_string const k_text_nobility_growth( "town_nobility_growth.misc" );
static t_external_string const k_text_building_growth( "town_building_growth.misc" );
static t_external_string const k_text_icon_label( "town_icon_label.misc" );

void t_town::display_growth_breakdown_window( t_town_building building, t_window *parent )
{
	// All growths are weekly * k_population_divisor
	t_creature_type               creature;
	int                           base_growth;
	int							  nobility_bonus;
	t_hero *					  nobility_hero;
	int							  pit_bonus;
	int							  grail_bonus;
	int							  total_growth;
	bool						  needs_decimal = false;
	t_player const *			  owner = get_owner();
	std::string					  grail_building_name;

	if (building == k_town_portal)
	{
		creature = m_portal_creature;
		base_growth = (k_portal_daily_charge * 7 * k_population_divisor) / get_traits( creature ).experience;

		needs_decimal = number_needs_decimal(base_growth);

		nobility_bonus = 0;
		pit_bonus = 0;
		grail_bonus = 0;

		total_growth = base_growth;
	}
	else
	{
		creature = get_traits( m_type, building ).creature;
		base_growth = get_traits( creature ).weekly_growth * k_population_divisor;

		int current_growth = base_growth;
		int prior_growth = current_growth;

		nobility_hero = get_best_nobility_hero();

		if (nobility_hero)
		{
			int bonus = nobility_hero->get_skill_power(k_skill_nobility);

			current_growth = (current_growth * (100 + bonus)) / 100;

			nobility_bonus = current_growth - prior_growth;

			needs_decimal = needs_decimal || number_needs_decimal(nobility_bonus);

			prior_growth = current_growth;
		}
		else
		{
			nobility_bonus = 0;
		}

		if (has( k_town_breeding_pit ))
		{
			current_growth += base_growth / 2;
			
			pit_bonus = current_growth - prior_growth;

			needs_decimal = needs_decimal || number_needs_decimal(pit_bonus);
			
			prior_growth = current_growth;
		}
		else
		{
			pit_bonus = 0;
		}

		if ( gets_global_grail_effects( k_town_might ) )
		{
			// Festival of Life gives 100% bonus to own town, 25% bonus to all other towns
			if ( has( k_town_grail ) && m_type == k_town_might )
				current_growth += base_growth;
			else
				current_growth += base_growth / 4;

			grail_bonus = current_growth - prior_growth;

			grail_building_name = get_traits( k_town_might, k_town_grail ).name;

			needs_decimal = needs_decimal || number_needs_decimal(grail_bonus);

			prior_growth = current_growth;
		}
		else
		{
			grail_bonus = 0;
		}


		total_growth = current_growth;
	}

	assert(total_growth == get_growth(building));

	std::string                   title_text;
	std::string                   body_text;
	std::string                   label_text;
	const char * const            growth_format = (needs_decimal ? "%.1lf" : "%.0lf"); // NOTE: Needs localization
	t_creature_traits			  creature_traits = get_traits( creature );

	title_text = replace_keywords(k_text_weekly_growth_title, "%Creature", creature_traits.name);

	body_text = replace_keywords(k_text_base_growth, "%growth", format_string(growth_format, double(base_growth) / double(k_population_divisor)));

	if (nobility_bonus)
	{
		body_text += "\n";
		body_text += replace_keywords(k_text_nobility_growth, 
									  "%growth", format_string(growth_format, double(nobility_bonus) / double(k_population_divisor)),
									  "%skill_name", ::get_name(t_skill(k_skill_nobility, nobility_hero->get_skill(k_skill_nobility))),
									  "%hero_name", nobility_hero->get_name());
	}

	if (pit_bonus)
	{
		t_building_traits const& traits = get_traits( m_type, k_town_breeding_pit );

		body_text += "\n";
		body_text += replace_keywords(k_text_building_growth,
									  "%growth", format_string(growth_format, double(pit_bonus) / double(k_population_divisor)),
									  "%Building", traits.name);
	}

	if (grail_bonus)
	{

		body_text += "\n";
		body_text += replace_keywords(k_text_building_growth,
									  "%growth", format_string(growth_format, double(grail_bonus) / double(k_population_divisor)),
									  "%Building", grail_building_name);
	}

	double growth = double(total_growth) / double(k_population_divisor);
	label_text = replace_keywords(k_text_icon_label, 
								  "%growth", format_string(growth_format, double(total_growth) / double(k_population_divisor)),
								  "%creatures", (growth > 1) ? creature_traits.plural_name : creature_traits.name);

	t_counted_ptr<t_basic_dialog> dialog = new t_basic_dialog(parent);

	dialog->set_title(title_text);
	dialog->set_text(body_text);
	dialog->add_ok_button();
	dialog->add_creature_icon(creature, label_text);

	dialog->open();
	dialog->run_modal();
}

// -----------------------------------------------------------
// Displayes a simple dialog explaining where all the growth
// for a creature comes from
// -----------------------------------------------------------
void t_town::display_growth_breakdown_window( t_creature_type creature, t_window *parent )
{
	t_creature_traits const& traits = get_traits( creature );

	if (traits.alignment != m_type)
		return;

	t_town_building building = traits.dwelling;
	int             dwelling = building - k_town_dwelling_1;

	assert( dwelling >= 0 && dwelling < k_dwelling_count );
	
	display_growth_breakdown_window( building, parent );
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
int	t_town::get_portal_population() const
{
	if (!has( k_town_portal ))
		return 0;
	return m_portal_charge / get_traits( m_portal_creature ).experience;
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
void t_town::preprocess_new_day()
{
	// do dwelling growth
	// Must do this BEFORE nobility heroes have a chance to recover hitpoints and 
	// maybe wake up if unconscious
	if (get_owner_number() >= 0)
	{
		if (m_building_delay > 0)
			m_building_delay--;

		t_town_building building;
		int				growth;

		for (building = k_town_dwelling_1; building <= k_town_dwelling_8; enum_incr(building))
		{
			if (!has( building ))
				continue;

			growth = get_growth( building ) / 7;
			m_population[building - k_town_dwelling_1] += growth;
		}

		if ( has( k_town_treasury ) )
		{
			// Determine amount treasury will give us today. Do NOT record the gain
			// yet; wait until process_new_day() so that treasuries do not
			// multiply
			int current_gold = get_owner()->get_funds()[k_gold];
			int current_gold_frac = get_owner()->get_fractional_funds()[k_gold];

			current_gold_frac += (current_gold << 8);

			m_last_treasury_bonus = 0.5 + (double(current_gold_frac) * k_per_day_treasury_growth);
		}
	}
}

static int get_creature_count( t_creature_array const& creatures, t_creature_type creature )
{
	// count creatures of this type
	int result = 0;
	int	i;

	for (i = 0; i < t_creature_array::k_size; ++i)
	{
		if (creatures[i].get_number() == 0)
			continue;
		if (creatures[i].get_creature_type() != creature)
			continue;
		result += creatures[i].get_number();
	}
	return result;
}

// -----------------------------------------------------------
// town data
// generate new guards for neutral towns
// -----------------------------------------------------------
void t_town::neutral_growth()
{
	t_town_building building;
	t_creature_type	creature_type;
	int				current_guards;
	int				new_guards;
	int				i;
	
	for (building = k_town_dwelling_1; building <= k_town_dwelling_8; enum_incr(building))
	{
		if (!has( building ))
			continue;
		creature_type = get_traits( m_type, building ).creature;
		if (!can_add( creature_type ))
			continue;

		current_guards = get_creature_count( *this, creature_type );
		new_guards = ((m_population[building - k_town_dwelling_1] * 2 / k_population_divisor ) - current_guards) / 3;
		if (new_guards <= 0)
			continue;
		add( creature_type, new_guards );
		m_population[building - k_town_dwelling_1] -= new_guards * k_population_divisor;
	}
	if (has( k_town_portal ))
	{
		std::vector<t_creature_type> const& creature_types = get_portal_creature_types();
		int									population;
				
		if (!can_add( m_portal_creature ) || m_map->get_day() % 10 == 0)
		{
			int count = 0;

			i = creature_types.size();
			while (i--)
			{
				if (can_add( creature_types[i] ))
					++count;
			}
			if (count == 0)
				return;
			count = random( count );
			i = creature_types.size();
			while (i--)
			{
				if (can_add( creature_types[i] ))
				{
					--count;
					if (count < 0)
					{
						m_portal_creature = creature_types[i];
						break;
					}
				}
			}
		}
		
		if (!can_add( m_portal_creature ))
			return;

		t_creature_traits const& traits = get_traits( m_portal_creature );
		
		current_guards = get_creature_count( *this, m_portal_creature );
		population = m_portal_charge / traits.experience;
		new_guards = (population * 2 - current_guards ) / 3;
		if (new_guards > 0)
		{
			m_portal_charge -= new_guards * traits.experience;
			add( m_portal_creature, new_guards );
		}
	}
}

// --------------------------------------------------------------------
// give spell points to heroes in army
// --------------------------------------------------------------------
static void give_spell_points( t_creature_array& army, int recovery, int new_maximum )
{
	int     i;
	t_hero* hero;
	int		recovered;
	int		limit;

	for (i = 0; i < t_creature_array::k_size; i++)
	{
		hero = army[i].get_hero();
		if (hero == 0)
			continue;
		if (hero->is_dead())
			hero->set_dead( false );
		hero->set_wounds( 0 );
		if (recovery == 0)
			continue;
		limit = hero->get_maximum_spell_points() * new_maximum / 100;
		recovered = limit * recovery / 100 + hero->get_spell_points();
		if (limit < recovered)
			recovered = limit;
		if (recovered > hero->get_spell_points())
			hero->set_spell_points( recovered );
	}
}

// --------------------------------------------------------------------
// choose a nearby visiting army
// --------------------------------------------------------------------
static void aid_adjacent_armies( t_town* town, int recovery, int limit )
{
	t_adventure_map* map = town->get_map();

	t_footprint const&		footprint =	town->get_footprint();
	t_level_map_point_2d	origin = town->get_position();
	t_level_map_point_2d	point;
	t_level_map_point_2d	center;
	t_map_point_2d			offset;
	t_map_point_2d			footprint_size = footprint.get_size();
	t_direction				direction;
	t_map_point_2d			direction_offset;
	t_player*				owner = town->get_owner();

	// look at each point in footprint, looking for trigger cells.
	for (offset.row = 0; offset.row < footprint_size.row; offset.row++)
	{
		for (offset.column = 0; offset.column < footprint_size.column; offset.column++)
		{
			if (!footprint.is_trigger_cell( offset ))
				continue;

			// compute the different ways a size x size square can overlap this trigger
			// move off the trigger in a given direction.
			for (direction = t_direction(0); direction < k_direction_count; 
				 enum_incr( direction ))
			{
				direction_offset = get_direction_offset( direction );

				// find the new point.
				point = origin + offset + direction_offset;

				// find out if this region is blocked.
				if (!map->is_valid( point ))
					continue;

				// look for an army in this square (not on bridges)
				t_adventure_tile const&				tile = map->get_adv_tile( point );
				bool							    look_on_bridge = tile.has_ramp(); // Armies on true bridges don't get benefits.
				
				const std::vector<int>& triggers = tile.get_triggers(look_on_bridge);
				for (std::vector<int>::const_iterator	index = triggers.begin(); index != triggers.end(); ++index)
				{
					t_army* army = dynamic_cast<t_army*>(&map->get_adv_object( *index ));

					if (army == 0)
						continue;

					if (army->is_boat())
						continue;	// Boats aren't allowed to "visit" towns.

					if ( !same_team( owner, army->get_owner() ) )
						continue;

					give_spell_points( *army, recovery, limit );
				}
			}
		}
	}
}


// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
void t_town::process_new_day()
{
	t_creature_array::process_new_day( false );
	t_owned_adv_object::process_new_day();

	if (m_tavern_delay > 0)
		--m_tavern_delay;

	if ( m_mana_vortex_delay > 0 )
		m_mana_vortex_delay--;

	// add mana recovery
	int recovery = 0;
	int new_maximum;

	if ( m_type == k_town_order && gets_global_grail_effects( k_town_order ) )
	{
		// Great Library boosts spell point recovery for order mage guilds. Heroes even exceed
		// their normal limits
		if (has( k_town_mage_guild_5 ))
		{
			recovery = 100;
			new_maximum = 200;
		}
		else if ( has( k_town_mage_guild_4 ))
		{
			recovery = 100;
			new_maximum = 175;
		}
		else if ( has( k_town_mage_guild_3 ))
		{
			recovery = 100;
			new_maximum = 150;
		}
		else if ( has( k_town_mage_guild_2 ))
		{
			recovery = 100;
			new_maximum = 125;
		}
		else if ( has( k_town_mage_guild_1 ))
		{
			recovery = 100;
			new_maximum = 100;
		}
	}
	else
	{
		new_maximum = 100;
		if (has( k_town_mage_guild_1 ))
			recovery = 100;
	}

	give_spell_points( *this, recovery, new_maximum );
	aid_adjacent_armies( this, recovery, new_maximum );

	if (has( k_town_portal ))
	{
		m_portal_charge += k_portal_daily_charge;
	}

	if (get_owner_number() < 0)
	{
		neutral_growth();
		return;
	}

	// compute gold production
	get_owner()->gain( k_gold, get_gold_production() );

	if ( has( k_town_treasury ) )
	{
		get_owner()->fractional_gain( k_gold, m_last_treasury_bonus );
	}
}

// Get gold production
int t_town::get_gold_production() const
{
    int gold;

	if (has( k_town_city_hall ))
		gold = 1000;
	else if (has( k_town_hall ))
		gold = 750;
	else
		gold = 500;

	if ( has( k_town_grail ) )
		gold += 3000;

    return (gold);
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
void t_town::destroy()
{
	if ( is_on_map() )
	{
		if (get_owner_number() >= 0)
			get_owner()->remove_town( this );
		m_map->remove( this );
	}

	t_abstract_town::destroy();
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
void t_town::place( t_adventure_map& map, t_adv_map_point const& point )
{
	t_abstract_town::place( map, point );
	set_terrain();
	setup_flag_color();
	if (get_owner_number() >= 0)
		get_owner()->add_town( this );

	if (m_single_use_id == -1 
		&& (has(k_town_grove) || has(k_town_arena) || has(k_town_academy)))
		m_single_use_id = map.get_new_single_use_id();

	if (m_training_grounds_use_id == -1 && has(k_town_training_grounds))
		m_training_grounds_use_id = map.get_new_single_use_id();


    // Add to total map income.
	t_town_building building = k_town_village_hall;
    while ( is_enabled(building) && (building <= k_town_city_hall) )
        enum_incr( building );

    int max_gold;
	if (building == k_town_city_hall)
		max_gold = 1000;
	else if (building == k_town_hall)
		max_gold = 750;
	else
		max_gold = 500;
    map.add_to_total_income ( max_gold );
    map.add( this );
}


// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
void t_town::set_start_buildings()
{
	if (has( k_town_fort ))
	{
		create_building( k_town_dwelling_1 );
		create_building( k_town_dwelling_2 );
		if (get_player_color() != k_player_gray && m_type != k_town_might)
			create_building( k_town_mage_guild_1 );
	}	
	else
		create_building( random( k_town_dwelling_1, k_town_dwelling_2 ) );
	if (get_player_color() != k_player_gray)
		create_building( k_town_tavern );
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
void t_town::set_start_creatures()
{
	if (has( k_town_fort ))
	{
		if (get_player_color() == k_player_gray)
			add_start_creature( *this, m_type, 
				                random( k_town_dwelling_3, k_town_dwelling_4 ) );
		add_start_creature( *this, m_type, k_town_dwelling_1 );
		add_start_creature( *this, m_type, k_town_dwelling_2 );
	}
	else
	{
		if (get_player_color() == k_player_gray)
		{
			add_start_creature( *this, m_type, k_town_dwelling_1 );
			add_start_creature( *this, m_type, k_town_dwelling_2 );
		}
		else
		{
			add_start_creature( *this, m_type, 
					            random( k_town_dwelling_1, k_town_dwelling_2 ));
		}
	}
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
void t_town::set_start_population()
{
	t_town_building building;

	for (building = k_town_dwelling_1; building <= k_town_dwelling_8; enum_incr( building ))
	{
		if (!has( building ))
			m_population[building - k_town_dwelling_1] = 0;
		else
		{
			t_creature_type creature = get_traits( m_type, building ).creature;
	
			m_population[building - k_town_dwelling_1] = get_traits( creature ).weekly_growth * k_population_divisor;
		}
	}
	m_portal_charge = k_portal_daily_charge * 7;
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
bool t_town::read_from_map( std::streambuf& stream, t_progress_handler* handler )
{
	bool		custom_garrison;
	bool		custom_buildings;
	bool        custom_skills;
	t_skill_set available_skills;
	t_skill_set required_skills;

	int version = get< t_uint16 >( stream );
	if ( version < 0 || version > k_current_map_format_version )
		return false;

	// Store some temporary data for use by initialize()
	m_temp_read_from_map_data = std::auto_ptr< t_temp_read_from_map_data >( new t_temp_read_from_map_data );


	if ( version >= 4 )
	{
		if (	!::read( stream, m_temp_read_from_map_data->m_required_spells )
			||	!::read( stream, m_temp_read_from_map_data->m_potential_spells )
			||	!t_abstract_town::read_map_data(
					stream,
					handler,
					custom_garrison,
					custom_buildings,
					custom_skills,
					m_temp_read_from_map_data->m_available_skills,
					m_temp_read_from_map_data->m_required_skills ) )
			return false;
	}
	else
	{
		m_temp_read_from_map_data->m_required_spells = k_town_default_required_spells;
		m_temp_read_from_map_data->m_potential_spells = k_town_default_potential_spells;

		if (	!t_abstract_town::read_map_data(
					stream,
					handler,
					custom_garrison, 
					custom_buildings,
					custom_skills,
					m_temp_read_from_map_data->m_available_skills,
					m_temp_read_from_map_data->m_required_skills,
					version ) )
			return false;
	}

	if ( !custom_buildings )
		set_start_buildings();
	if ( !custom_garrison )
		set_start_creatures();
	m_buildings.set( k_town_village_hall );
	m_buildings.set( k_town_prison );
	set_start_population();

	if ( !custom_skills )
	{
		m_temp_read_from_map_data->m_available_skills.set();
		m_temp_read_from_map_data->m_required_skills.reset();
	}

	t_skill_set const & possible_skills = get_possible_town_school_skills( get_type() );

	m_temp_read_from_map_data->m_available_skills &= possible_skills;
	m_temp_read_from_map_data->m_required_skills &= possible_skills;

	m_last_treasury_bonus = 0;
	m_mana_vortex_delay = 0;
	visit_town(this);

	return true;
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
bool t_town::preplacement( t_adventure_map& map, int pass )
{
	if ( pass == 0 )
	{
		if (m_named_by_map)
			map.add_mapped_name( get_name(), this );


		for (int i = 0; i < m_prisoner_list.size(); i++)
		{
			t_hero_ptr hero_ptr = m_prisoner_list[ i ].hero;

			if (hero_ptr->was_named_by_map())
				map.add_mapped_name( hero_ptr->get_name(), hero_ptr );
		}
	}

	return t_abstract_town::preplacement( map, pass );
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
void t_town::set_owner( int player_number )
{
	if (get_owner() != 0)
		get_owner()->remove_town( this );

	t_abstract_town::set_owner( player_number );
	
	if (get_owner() != 0)
		get_owner()->add_town( this );

	if (get_map()) 
	{
		process_timed_events(*get_map(), false);
	}

	m_last_treasury_bonus = 0; // Don't let new owner see treasury amount; might give them clues to original player's wealth
}

// -----------------------------------------------------------
// Check for disallowed mage guilds in might towns
// -----------------------------------------------------------
void t_town::disallow_might_mage_guilds()
{
	if ( get_type() == k_town_might )
	{
		// No mage guilds in might towns
		t_town_building building;

		for ( building = k_town_mage_guild_1; building <= k_town_library_2; enum_incr( building ) )
		{
			m_enabled_buildings.reset( building );
			m_buildings.reset( building );
		}
	}
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
void t_town::initialize( t_adventure_map& map )
{
	t_abstract_town::initialize( map );

	if ( get_owner_number() >= 0 )
	{
		get_owner()->add_town( this );
	}

	disallow_might_mage_guilds();

	// Note that if we were originally a random town, we never did a read_from_map and
	// m_temp_read_from_map_data is NULL
	if ( m_temp_read_from_map_data.get() != NULL )
	{
		// Initialize spells & skills. Note that we can't do this earlier (like in read_from_map())
		// because we need the map to give us the global list of disallowed spells & skills

		create_spells( m_temp_read_from_map_data->m_required_spells,
					   m_temp_read_from_map_data->m_potential_spells, 
					   map );
		setup_school_skills( m_temp_read_from_map_data->m_available_skills, 
							 m_temp_read_from_map_data->m_required_skills, 
							 map.get_allowed_skills() );

		// Done with temp data
		m_temp_read_from_map_data.reset();
	}

	assert( !m_school_skills.empty() || ( get_type() != k_town_order && get_type() != k_town_life ) ); 
	assert( !m_spells.empty() || get_type() == k_town_might );

	update_spells();

	// check if a shipyard is legal
	if (m_enabled_buildings[k_town_shipyard])
	{
		t_adv_map_point point;
		
		if (!find_new_ship_position( point, true ))
			m_enabled_buildings.reset( k_town_shipyard );
	}

	
	if (m_map != NULL)
		m_map->cache_adventure_object_state( this );
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
void t_town::update_state()
{
	t_abstract_town::update_state();
	update_model();
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
bool t_town::update_model()
{
	t_town_image_level	image_level = get_castle_level();
	t_two_way_facing	facing = t_two_way_facing( get_minor_subtype() );
	char const*			new_name;
	
	new_name = get_town_model_name( m_type, image_level, facing );

	if (stricmp( new_name, get_model_name().c_str() ) == 0)
		return false;
	
	t_float_object floater( this );
	
	set_model( new_name );

	if (m_map!=NULL)
		m_map->record_update_event( this );

	return true;
}

// -----------------------------------------------------------
// Helper function for t_town::create_spells() below
// -----------------------------------------------------------
void add_spell( t_spell					spell,
				t_guild_spell_list &	spell_list,
				t_spell_set&			used,
				t_adventure_map&		map )
{
	assert( spell >= 0 && spell < k_spell_count );
	assert( is_teachable( spell ) );
	assert( !used[ spell ] );

	t_guild_spell new_entry;

	new_entry.spell = spell;
	new_entry.available = false;
	spell_list.push_back( new_entry );

	used[ spell ] = true;
	map.increment_spell_count( spell );
}

// -----------------------------------------------------------
// choose a spell to add at random
// -----------------------------------------------------------
inline void add_random_spell( t_guild_spell_list&	spells,
							  t_spell_set&			candidates,
							  t_spell_set&			used,
							  t_adventure_map&		map )
{
	int	choice = random( candidates.count() );

	t_spell spell;

	for (spell = t_spell(0); spell < k_spell_count; enum_incr( spell ))
	{
		if (!candidates[spell])
			continue;
		if (choice-- == 0)
			break;
	}
	add_spell( spell, spells, used, map );
}

// -----------------------------------------------------------
// choose a spell at random to add to spells
// -----------------------------------------------------------
static bool select_new_spell(  t_guild_spell_list&	spells, 
							   t_spell_set&			used,
							   t_spell_set const&	allowed_spells,
							   t_adventure_map&		map )
{

	t_spell_set	candidates = allowed_spells;

	candidates &= ~used;
	if (candidates.none())
		return false;

	candidates = map.select_spells( candidates );
	add_random_spell( spells, candidates, used, map );
	return true;
}

// -----------------------------------------------------------
// add spells which are high priority
// -----------------------------------------------------------
static bool add_high_priority_spell( t_guild_spell_list&	spells, 
									 t_spell_set&			used,
									 t_spell_set const&		allowed_spells,
									 t_adventure_map&		map )
{
	t_spell			spell;
	t_spell_set	candidates;

	for (spell = t_spell(0); spell < k_spell_count; enum_incr( spell ))
	{
		if ( !allowed_spells[ spell ] || !is_high_priority( spell ))
			continue;
		if (used[spell])
			return false;
		candidates[spell] = true;
	}
	if (candidates.none())
		return false;
	add_random_spell( spells, candidates, used, map );
	return true;
}

// -----------------------------------------------------------
// get set of all spells that match an alignment and level
// -----------------------------------------------------------
static void get_spells_of_level( t_spell_set& result, t_town_type alignment, int level )
{
	t_spell spell;
	
	for (spell = t_spell(0); spell < k_spell_count; enum_incr( spell ))
	{
		result[spell] = get_spell_alignment( spell ) == alignment
					 && get_spell_level( spell ) == level
					 && is_teachable( spell );
	}
}


// -----------------------------------------------------------
// town data
// create the spells which will eventually fill the mage guild
// -----------------------------------------------------------
void t_town::create_spells( t_spell_set const & required_spells,
						    t_spell_set const & potential_spells,
							t_adventure_map&	map )
{
	assert( m_spells.empty() );

	if (m_type == k_town_might)
		return;

	t_spell_set const &			global_allowed_spells = map.get_allowed_spells();
	int							level;
	t_alignment_relationship	relationship;
	t_town_type					alignment;
	int							count;
	t_spell						spell;
	t_spell_set					used;
	t_guild_spell_list			new_spells;

	used.reset();
	for (level = 1; level <= 5; level++)
	{
		for (alignment = t_town_type(0); alignment < k_town_might; enum_incr( alignment ))
		{
			relationship = get_alignment_relationship( m_type, alignment );
			if (relationship > k_alignments_allied)
				continue;

			count = get_mage_guild_spell_count( level, relationship == k_alignments_allied );

			// first, add all of the required spells of this level and alignment
			for (spell = t_spell(0); spell < k_spell_count; enum_incr( spell ))
			{
				if (	!required_spells[ spell ]
					||	get_spell_alignment( spell ) != alignment
					||	get_spell_level( spell ) != level )
					continue;
				add_spell( spell, new_spells, used, map );
				--count;
				if (count == 0)
					break;
			}
			assert( count >= 0 );

			t_spell_set all_spells;
			t_spell_set allowed_spells;

			get_spells_of_level( all_spells, alignment, level );
			allowed_spells = all_spells;
			allowed_spells &= potential_spells;
			allowed_spells &= global_allowed_spells;
			if (count > 0 && add_high_priority_spell( new_spells, used, allowed_spells, map ))
				--count;

			// now randomly pick the rest of the spells
			while (count)
			{
				if (!select_new_spell( new_spells, used, allowed_spells, map ))
					break;
				--count;
			}
			if (count)
			{
				allowed_spells = all_spells;
				allowed_spells &= potential_spells;

				// try again, but allow globally prohibited spells
				while (count)
				{
					if (!select_new_spell( new_spells, used, allowed_spells, map ))
						break;
					--count;
				}
				// try again, but allow all normally legal spells
				while (count)
				{
					if (!select_new_spell( new_spells, used, all_spells, map ))
						break;
					--count;
				}
			}
		}
	}

	m_spells.swap( new_spells );
}

// -----------------------------------------------------------
// town data
// get the alignment of a library
// -----------------------------------------------------------
t_town_type t_town::get_library_type( t_town_building building ) const
{
	static t_town_type const k_library_type[k_town_type_count][2] =
	{
		{ k_town_nature, k_town_order },
		{ k_town_life,	 k_town_death },
		{ k_town_order,  k_town_chaos },
		{ k_town_death,  k_town_nature },
		{ k_town_chaos,  k_town_life },
		{ k_town_type_count,  k_town_type_count },
	};

	if (building >= k_town_library_1 && building <= k_town_library_2)
		return k_library_type[m_type][building - k_town_library_1];
	if (building >= k_town_mage_guild_1 && building <= k_town_mage_guild_5)
		return m_type;
	assert( false );
	return k_town_type_count;
}

// ---------------------------------------------------------------
// town data
// Show army info.
// ---------------------------------------------------------------
void t_town::right_click( t_mouse_event const& event,
		                  t_adventure_frame* adventure_frame )
{
	run_army_info_window( &get_garrison(), adventure_frame );
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
void t_town::update_spells()
{
	if (m_type == k_town_might)
		return;

	if ( m_temp_read_from_map_data.get() != NULL )
	{
		// HACK!
		// We have read in the town, but can't choose the spells until initialize() is called,
		// so just return for now. initialize() will call update_spells() after choosing them
		return; 
	}

	// create_spells() must have been called previously
	assert( !m_spells.empty() );

	int  level;
	int  i;
	bool alignments[k_town_type_count];


	for (level = 5; level > 0; level--)
		if (has( t_town_building( k_town_mage_guild_1 + level - 1 )))
			break;

	if (level == 0)
		return;

	if ( m_type == k_town_order && gets_global_grail_effects( k_town_order ) )
	{
		// Great Library ensures all order guilds have 5th level spells
		level = 5;
	}

	memset( alignments, 0, sizeof( alignments ));
	alignments[m_type] = true;
	for (i = 0; i < 2; i++)
		if (has( t_town_building(k_town_library_1 + i) ))
			alignments[get_library_type( t_town_building( k_town_library_1 + i ) )] = true;
	for (i = 0;  i < m_spells.size(); i++)
	{
		if (get_spell_level( m_spells[i].spell ) > level)
			continue;
		if (!alignments[get_spell_alignment(m_spells[i].spell)])
			continue;
		m_spells[i].available = true;
	}
}

/*----------------------------------------------------------------------------------*\
**
** Set available seminary or university skills - called once when town is created.
**
\*----------------------------------------------------------------------------------*/
void t_town::setup_school_skills( t_skill_set local_available_skills, t_skill_set local_required_skills, t_skill_set global_allowed_skills )
{
	int count = 0;

	m_school_skills.clear();
	t_skill_set possible_skills = get_possible_town_school_skills( get_type() );

	if ( possible_skills.none() )
		return; // Town is not order or life & has no school

	local_available_skills &= possible_skills;
	local_required_skills &= possible_skills;
	global_allowed_skills &= possible_skills;
	
	t_skill_set available_skills = local_available_skills & global_allowed_skills;

	while ( count < k_school_skill_count && possible_skills.any() )
	{
		t_skill_set const * choosing_set; // Set of skills to choose from
		
		if ( local_required_skills.any() )
		{
			// Note: required spells and skills override the map's disallowed list -- specific 
			// overrides general
			choosing_set = &local_required_skills;
		}
		else if ( available_skills.any() )
		{
			choosing_set = &available_skills;
		}
		else if ( local_available_skills.any() )
		{
			choosing_set = &local_available_skills;
		}
		else if ( global_allowed_skills.any () )
		{
			choosing_set = &global_allowed_skills;
		}
		else
		{
			choosing_set = &possible_skills;
		}

		int index = random( choosing_set->count() );

		t_skill_type skill = t_skill_type(0);

		while ( !choosing_set->at( skill ) )
		{
			enum_incr( skill );
		}

		while ( index )
		{
			enum_incr( skill );

			while ( !choosing_set->at( skill ) )
			{
				enum_incr( skill );
			}

			index--;
		}

		m_school_skills.push_back( skill );

		local_required_skills[ skill ] = false;
		local_available_skills[ skill ] = false;
		available_skills[ skill ] = false;
		global_allowed_skills[ skill ] = false;
		possible_skills[ skill ] = false;

		count++;
	}

	assert( local_required_skills.none() );

	assert( count == k_school_skill_count );
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
bool t_town::write( std::streambuf& stream ) const
{
	int i;

	t_abstract_town::write( stream );
	::write( stream, m_population, sizeof( m_population ));
	put<t_terrain_type>( stream, m_terrain );
	put<t_uint8>( stream, m_spells.size() );
	for (i = 0; i < m_spells.size(); i++)
		::write( stream, &m_spells[i], sizeof( t_guild_spell ));
	put<t_int16>( stream, m_portal_creature );
	put<int>( stream, m_portal_charge );
	put<t_uint8>( stream, m_tavern_delay );

	put<t_uint8>( stream, m_school_skills.size() );
	for (i = 0; i < m_school_skills.size(); i++)
    	put<t_uint8>( stream, (t_uint8)m_school_skills[i] );
	
	assert(!(has(k_town_grove) && has(k_town_arena)));
	assert(!(has(k_town_academy) && has(k_town_arena)));
	assert(!(has(k_town_academy) && has(k_town_grove)));
	
	if (has(k_town_grove) || has(k_town_arena) || has(k_town_academy))
		put<int>(stream, m_single_use_id);

	if (has(k_town_training_grounds))
		put<int>(stream, m_training_grounds_use_id);

	put<t_uint8>( stream, m_prisoner_list.size() );
	for (i = 0; i < m_prisoner_list.size(); i++)
    {
		put<int>(stream, m_prisoner_list[i].owner_number );
	    m_prisoner_list[i].hero->write( stream );
    }

	put< t_uint32 >( stream, m_last_treasury_bonus );
	put< t_uint8 >( stream, m_mana_vortex_delay );
	put< t_uint8 >( stream, m_building_delay );

	return true;
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
int t_town::get_version() const
{
	return 19;
}

// -----------------------------------------------------------
// town data
// -----------------------------------------------------------
bool t_town::read( std::streambuf&						stream, 
				   t_qualified_adv_object_type const&	type,
				   int									version )
{
	init();

	int           count;
	t_guild_spell spell;

	t_abstract_town::read( stream, type, version );
	::read( stream, m_population, sizeof( m_population ));

	if ( version < 17 )
	{
		// Old population divisor
		int i;
		for ( i = 0; i < k_dwelling_count; i++ )
		{
			if ( m_population[i] < 0 )
				return false;

			m_population[i] = int( ( unsigned int( m_population[i] ) * k_population_divisor ) >> 8 );
		}
	}

	m_terrain = get<t_terrain_type>( stream );
	count = get<t_uint8>( stream );
	m_spells.clear();
	while (count--)
	{
		::read( stream, &spell, sizeof(spell));
		m_spells.push_back( spell );
	}
	if (version < 1)
	{
		m_portal_creature = k_default_portal_creature;
		if (has( k_town_portal ))
			m_portal_charge = k_portal_daily_charge * 7 / 2;
	}
	else
	{
		m_portal_creature = t_creature_type( get<t_int16>( stream ));
		m_portal_charge = get<int>( stream );
	}
	if (version < 2)
		m_tavern_delay = 0;
	else
		m_tavern_delay = get<t_uint8>( stream );
  
    /*
    ** Set seminary skills, or read from save file.
    */
	if ( version < 11 )
	{
		// Note: previous versions of the save files recorded seminary & university skills
		// separately. Combine back into our single list

		t_skill_list seminary_skill_list;
		bool seminary_list_set;

		if (version < 3)
		{
			seminary_list_set = false;
		}
		else
		{
			seminary_list_set = true;
			count = get<t_uint8>( stream );
   			
			seminary_skill_list.reserve( count );
			
			while (count--)
    		{
    			seminary_skill_list.push_back( t_skill_type( get<t_uint8>( stream )) );
			}
		}

		t_skill_list university_skill_list;
		bool university_list_set;

		/*
		** Set university skills, or read from save file.
		*/
		if (version < 4)
		{
			university_list_set = false;
		}
		else
		{
			university_list_set = true;

			count = get<t_uint8>( stream );
   			university_skill_list.reserve( count );

			while (count--)
    		{
    			university_skill_list.push_back( t_skill_type( get<t_uint8>( stream )) );
			}
		}

		if ( get_type() == k_town_life || get_type() == k_town_order )
		{
			bool need_new_list;

			if ( get_type() == k_town_life )
			{
				need_new_list = !seminary_list_set;
			}
			else
			{
				need_new_list = !university_list_set;
			}

			if ( need_new_list )
			{
				// Note: anything old enough to be this version in the save file
				// is going to be too old to have a allowed_skills set in the map
				// So ignore the allowed skills and just choose from the 
				// possible skill set
				t_skill_set available_skills, required_skills, global_allowed_skills;
				available_skills.set();
				global_allowed_skills.set();

				setup_school_skills( available_skills, required_skills, global_allowed_skills );
			}
			else
			{
				if ( get_type() == k_town_life )
				{
					m_school_skills = seminary_skill_list;
				}
				else
				{
					m_school_skills = university_skill_list;
				}
			}
		}
	}
	else
	{
		count = get<t_uint8>( stream );
   		m_school_skills.reserve( count );

		while (count--)
    	{
    		m_school_skills.push_back( t_skill_type( get<t_uint8>( stream )) );
		}
	}

	// Read in single use ids
	assert(!(has(k_town_grove) && has(k_town_arena)));
	assert(!(has(k_town_academy) && has(k_town_arena)));
	assert(!(has(k_town_academy) && has(k_town_grove)));
		
	if (version < 7)
	{
		m_single_use_id = -1;
		m_training_grounds_use_id = -1;
	}
	else
	{
		if (has(k_town_grove) || has(k_town_arena) || has(k_town_academy))
			m_single_use_id = get<int>(stream);

		if (has(k_town_training_grounds))
			m_training_grounds_use_id = get<int>(stream);
	}

	if (version == 8 || version == 9)
		bool old_named_by_map = get< bool >( stream );

	if ( version < 12 )
    {
	    m_prisoner_list.clear();
    }
    else
    {
	    count = get<t_uint8>( stream );
	    m_prisoner_list.clear();
	    while (count--)
    	{
    	    t_town_prisoner prisoner;
            t_hero *        hero_ptr = new t_hero;

            prisoner.hero = hero_ptr;
            prisoner.owner_number = get<int>(stream);
            prisoner.hero->read( stream );

    		m_prisoner_list.push_back( prisoner );
    	}
    }

	if ( version < 13 )
	{
		m_last_treasury_bonus = 0;
	}
	else
	{
		m_last_treasury_bonus = get< t_uint32 >( stream );
	}

	if ( version < 14 )
	{
		m_mana_vortex_delay = 0;
	}
	else
	{
		m_mana_vortex_delay = get< t_uint8 >( stream );
	}

	if ( version < 18 ) {
		m_building_delay = 0;
	} else {
		m_building_delay = get< t_uint8 >( stream );
	}

	// Fix existing save files with invisible mage guilds in might towns
	disallow_might_mage_guilds();

	return true;
}

// -----------------------------------------------------------
// -----------------------------------------------------------
void t_town::execute_script(t_built_in_event_id event_id, t_army* army)
{
	assert( event_id >= 0 && event_id < k_built_in_event_count );
	assert( army != 0 );

	t_built_in_event_ptr & event_ptr = m_built_in_events[ event_id ];

	if (!event_ptr->ownership_test(get_owner())) return;

	t_adventure_frame* frame = get_adventure_frame();

	event_ptr->execute_effects(get_map()->get_current_player(), frame);
	frame->update();

	t_built_in_event::t_script const & script = event_ptr->get_script();
	script.execute( t_script_context_town( get_map(), this, this, army, army->get_owner(), &army->get_position() ) );
	if( get_map()->is_game_over() )
		return;

	frame->update();
	post_execute_validate(this);
	post_execute_validate(army);
		
	if ( script.pending_removal() ) {
		script.clear_removal();
		event_ptr = new t_built_in_event;
	}

	get_map()->process_continuous_events(frame);
	if( get_map()->is_game_over() )
		return;

	frame->update();
}

// -----------------------------------------------------------
// -----------------------------------------------------------
bool t_town::process_timed_events( t_adventure_map& map, bool start_of_day )
{
	bool result = false;

	int size = m_timed_events.size();
	for (int i = 0; i < size; i++) {
		t_timed_event_ptr& event = m_timed_events[i];
		if ( event->is_active_on_day(map.get_day()) 
			&& (&map.get_current_player() == get_owner())
			&& event->ownership_test(get_owner())
			&& (start_of_day || !event->has_executed()) )
		{
			result = true;

			event->set_execution_flag();
			event->execute_effects(map.get_current_player(), get_adventure_frame());
			get_adventure_frame()->update();

			t_timed_event::t_script const & script = event->get_script();
			script.execute(t_script_context_town(&map, this, this, NULL, NULL, &get_position()));
			if( map.is_game_over() )
				return false;

			get_adventure_frame()->update();
			post_execute_validate(this);

			if (script.pending_removal()) {
				script.clear_removal();
				event = new t_ownable_timed_event;
			}
		}
		else
			if (start_of_day)
				event->clear_execution_flag();
	}

	return result;
}

// -----------------------------------------------------------
// -----------------------------------------------------------
bool t_town::process_continuous_events( t_adventure_map& map )
{
	bool result = false;

	int size = m_continuous_events.size();
	for (int i = 0; i < size; i++) {
		t_continuous_event_ptr & event = m_continuous_events[i];
		if (( !event->get_run_only_during_owners_turn() || &map.get_current_player() == get_owner() )
			&& event->ownership_test(get_owner()) )
		{
			result = true;

			t_continuous_event::t_script const & script = event->get_script();
			script.execute(t_script_context_town(&map, this, this, NULL, NULL, &get_position()));
			if( map.is_game_over() )
				return false;

			get_adventure_frame()->update();
			post_execute_validate(this);

			if (script.pending_removal()) {
				script.clear_removal();
				event = new t_ownable_continuous_event;
			}
		}
	}

	return result;
}

// -----------------------------------------------------------
// -----------------------------------------------------------
bool t_town::process_triggerable_events( t_adventure_map& map, std::string const& name )
{
	bool result = false;

	int size = m_triggerable_events.size();
	for (int i = 0; i < size; i++) {
		t_triggerable_event_ptr & event = m_triggerable_events[i];
		if (( string_insensitive_compare(event->get_name(), name) == 0 )
			&& event->ownership_test(get_owner()) )
		{
			result = true;

			event->execute_effects(map.get_current_player(), get_adventure_frame());
			get_adventure_frame()->update();
			
			t_triggerable_event::t_script const & script = event->get_script();
			script.execute(t_script_context_town(&map, this, this, NULL, NULL, &get_position()));
			if( map.is_game_over() )
				return false;

			get_adventure_frame()->update();
			post_execute_validate(this);

			if (script.pending_removal()) {
				script.clear_removal();
				event = new t_ownable_triggerable_event;
			}
		}
	}

	return result;
}


// ---------------------------------------------------------------
// Helper function to check all points in a rect
// ---------------------------------------------------------------
static bool find_new_ship_position( t_adventure_map const * map, 
								    t_adv_map_point& result,
									t_map_rect_2d const& rect,
									bool ignore_removable)
{
	for (result.row = rect.top(); result.row < rect.bottom(); ++result.row)
	{
		for (result.column = rect.left(); result.column < rect.right(); ++result.column)
		{
			if ( map->can_place_new_boat( result, ignore_removable ) )
				return true;
		}
	}
	return false;
}

// ---------------------------------------------------------------
// Determine start position for a boat.
// Strongly related to t_adv_shipyard::find_new_ship_position()
// check all potential ship locations within range of our 
// building footprint.  Rectangles are arranged to give preference
// to more "visible" (ie: southerly) locations.
// You'll have to draw a picture on graph paper or just trust me.
// The positive row axis is down, and the positive column axis is
// right.  The 0,0 corner is "North" when drawn (ie rotated
// 45 degrees)
// ---------------------------------------------------------------
bool t_town::find_new_ship_position( t_adv_map_point & result, bool ignore_removable ) const
{
	t_map_rect_2d		 rect;
	t_adv_map_point      position = get_position();
	t_map_point_2d       footprint_size = get_footprint().get_size();

	result.level = get_position().level;
	result.on_bridge = false;

	// search South/West edge (bottom with regard to row/column axes)
	rect.top_left.row = position.row + footprint_size.row;
	rect.top_left.column = position.column - k_placement_search_range;
	rect.bottom_right.row = rect.top_left.row + k_placement_search_range;
	rect.bottom_right.column = position.column + footprint_size.column + k_placement_search_range;
	if (::find_new_ship_position( m_map, result, rect, ignore_removable ))
		return true;

	// search South/East edge (right with regard to row/column axes)
	rect.top_left.row = position.row - k_placement_search_range;
	rect.top_left.column = position.column + footprint_size.column;
	rect.bottom_right.row = position.row + footprint_size.row;
	rect.bottom_right.column = rect.top_left.column + k_placement_search_range;
	if (::find_new_ship_position( m_map, result, rect, ignore_removable ))
		return true;

	// search North/West edge (left with regard to row/column axes)
	rect.top_left.row = position.row;
	rect.top_left.column = position.column - k_placement_search_range;
	rect.bottom_right.row = position.row + footprint_size.row;
	rect.bottom_right.column = position.column;
	if (::find_new_ship_position( m_map, result, rect, ignore_removable ))
		return true;

	// search North/East edge (top with regard to row/column axes)
	rect.top_left.row = position.row - k_placement_search_range;
	rect.top_left.column = position.column - k_placement_search_range;
	rect.bottom_right.row = position.row;
	rect.bottom_right.column = position.column + footprint_size.column;
	if (::find_new_ship_position( m_map, result, rect, ignore_removable ))
		return true;

	return false;
}


// -------------------------------------------------------------------------------
// Add the points in the given rectangle to the list of adventure path points
// -------------------------------------------------------------------------------
static void add_pathing_destinations( t_adventure_map const * map, 
									  int level,
									  t_adventure_path_point const& source,
									  t_adventure_path_finder & path_finder,
									  t_map_rect_2d const& rect )
{
	t_adv_map_point new_point;

	new_point.on_bridge = false;
	new_point.level = level;
	for (new_point.row = rect.top(); new_point.row < rect.bottom(); ++new_point.row)
	{
		for (new_point.column = rect.left(); new_point.column < rect.right(); ++new_point.column)
		{
			if ( map->can_place_new_boat( new_point, false ) )
			{
				path_finder.push( new_point, source, source.move_remaining, 0, 0.0, k_army_path_state_ship );
			}
		}
	}
}

// -------------------------------------------------------------------------------
// virtual function used by the pathfinder to determine if an object
// should place additonal points into the path search.
// ---------------------------------------------------------------
// Largly dupplicate of find_new_ship_position, but instead
// this places all the points a ship could be built into
// the path finders list of reachable points.
// BE SURE TO KEEP THIS IN SYNC WITH t_town::find_new_ship_position()
// ---------------------------------------------------------------
void t_town::pathing_destination_query( t_adventure_path_point const& source, 
										t_adventure_path_finder & path_finder ) const
{
	// This only applies if the town has a shipyard
	if ( !has( k_town_shipyard ) )
		return;

	t_path_search_type type = path_finder.get_path_type();

	if ((type == k_path_search_ai) && !same_team(get_owner(), path_finder.get_army()->get_owner()))
	{

		t_adventure_tile const& tile = get_map()->get_adv_tile( source );
		t_adv_map_point ship_position;
		ship_position.on_bridge = false;
		if (find_new_ship_position( ship_position, false ))
			path_finder.push( ship_position, source, source.move_remaining, 0, get_ai()->get_ship_construction_barrier(), k_army_path_state_ship );

	}
	else if (type == k_path_search_caravan)
	{	

		// Special handling for caravans -- the player's shipyards act as a zero-resource-cost gateway to 
		// the water squares.
		// This part is largly copies from find_new_ship_position above and should remain in sync with it.
		
		if ( source.pathing_state != k_army_path_state_normal )
			return;

		if ( !same_team( path_finder.get_army()->get_owner(), get_owner() ) )
			return;

		t_map_rect_2d		 rect;
		t_level_map_point_2d position = get_position();
		t_map_point_2d       footprint_size = get_footprint().get_size();


		// search South/West edge (bottom with regard to row/column axes)
		rect.top_left.row = position.row + footprint_size.row;
		rect.top_left.column = position.column - k_placement_search_range;
		rect.bottom_right.row = rect.top_left.row + k_placement_search_range;
		rect.bottom_right.column = position.column + footprint_size.column + k_placement_search_range;
		::add_pathing_destinations( m_map, position.level, source, path_finder, rect );

		// search South/East edge (right with regard to row/column axes)
		rect.top_left.row = position.row - k_placement_search_range;
		rect.top_left.column = position.column + footprint_size.column;
		rect.bottom_right.row = position.row + footprint_size.row;
		rect.bottom_right.column = rect.top_left.column + k_placement_search_range;
		::add_pathing_destinations( m_map, position.level, source, path_finder, rect );

		// search North/West edge (left with regard to row/column axes)
		rect.top_left.row = position.row;
		rect.top_left.column = position.column - k_placement_search_range;
		rect.bottom_right.row = position.row + footprint_size.row;
		rect.bottom_right.column = position.column;
		::add_pathing_destinations( m_map, position.level, source, path_finder, rect );

		// search North/East edge (top with regard to row/column axes)
		rect.top_left.row = position.row - k_placement_search_range;
		rect.top_left.column = position.column - k_placement_search_range;
		rect.bottom_right.row = position.row;
		rect.bottom_right.column = position.column + footprint_size.column;
		::add_pathing_destinations( m_map, position.level, source, path_finder, rect );

	}
}


// ---------------------------------------------------------------
// attempt to build a boat
// ---------------------------------------------------------------
bool t_town::build_ship() const
{
	t_adv_map_point position;

	if (!find_new_ship_position( position, false ))
		return false;

	t_counted_ptr<t_army> boat;

	boat = new t_army( m_type );
	boat->set_owner( get_owner_number() );
	boat->place( *get_map(), position );

	t_adventure_frame* frame = get_adventure_frame();

	frame->reset_pathfinder();
	frame->update();
	return true;
}

// Add prisoner to town.
void t_town::add_prisoner( t_town_prisoner* prisoner )
{
	prisoner->hero->set_imprisoned( true );
	m_prisoner_list.push_back( *prisoner ); 
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_town::read_postplacement( t_adventure_map& map )
{
	t_abstract_town::read_postplacement( map );

	if (m_named_by_map) 
		map.add_mapped_name( get_name(), this );

	for (int i = 0; i < m_prisoner_list.size(); i++)
	{
		t_hero_ptr hero_ptr = m_prisoner_list[ i ].hero;

		if (hero_ptr->was_named_by_map())
			map.add_mapped_name( hero_ptr->get_name(), hero_ptr );
	}
}


// ---------------------------------------------------------------
// Use up the mana vortex
// ---------------------------------------------------------------
void t_town::use_up_mana_vortex() 
{
	m_mana_vortex_delay = k_mana_vortex_delay;
}

static t_external_string const k_arrived_caravan_killed_both_victor( "caravan_arrived.killed_both_victor" );
static t_external_string const k_arrived_caravan_killed_both_loser( "caravan_arrived.killed_both_loser" );
static t_external_string const k_arrived_caravan_killed_heroes_victor( "caravan_arrived.killed_heroes_victor" );
static t_external_string const k_arrived_caravan_killed_heroes_loser( "caravan_arrived.killed_heroes_loser" );
static t_external_string const k_arrived_caravan_killed_creatures_victor( "caravan_arrived.killed_creatures_victor" );
static t_external_string const k_arrived_caravan_killed_creatures_loser( "caravan_arrived.killed_creatures_loser" );

namespace
{
	struct t_caravaning_hero
	{
		t_hero_ptr hero;
		t_caravan_ptr caravan;

		t_caravaning_hero( t_hero_ptr _hero, t_caravan_ptr _caravan );
	};

	inline t_caravaning_hero::t_caravaning_hero( t_hero_ptr _hero, t_caravan_ptr _caravan )
		: hero( _hero ), caravan( _caravan )
	{
	}

	typedef std::vector< t_caravaning_hero > t_caravaning_hero_vector;
}

// ---------------------------------------------------------------
// Laugh a big evil laugh and kill any arrived caravans belonging to 
// other players after the town is captured. (That's what they get
// for leaving them in the caravan building...) 
// ---------------------------------------------------------------
void t_town::kill_arrived_caravans( t_army * victorious_army )
{
	t_caravaning_hero_vector heroes;
	t_caravaning_hero_vector::iterator hero_iter;

	bool any_creatures = false;
	typedef std::set< t_player * > t_player_set;
	t_player_set caravan_owners;

	// Not a reference so that we can call t_adventure_map::remove_caravans
	t_caravan_set caravans = m_map->get_caravans();

	t_caravan_set::iterator it;

	for ( it = caravans.get_lower_bound_of_destination( this ); it != caravans.end() && (*it)->get_destination().get() == this; it++ )
	{
		if ( !(*it)->has_arrived() )
			continue;

		// We do actually get caravans owned by ourselves here, in the case that several caravans 
		// arrive in an enemy-owned town, they do a forced attack, and the first one wins....
		// Don't kill our own caravans

		if ( !same_team( (*it)->get_owner(), get_owner() ) )
		{
			caravan_owners.insert( (*it)->get_owner() );

			t_creature_array & creatures = **it;

			int i;
			for ( i = 0; i < t_creature_array::k_size; i++ )
			{
				t_creature * creature = creatures[i].get_creature();
				t_hero     * hero     = creatures[i].get_hero();
				if ( creature )
				{
					any_creatures = true;
				}
				else if ( hero )
				{						
					heroes.push_back( t_caravaning_hero( hero, *it ) );
					t_town_prisoner prisoner;
					prisoner.hero = hero;
					prisoner.owner_number = (*it)->get_owner_number();
					add_prisoner( &prisoner );
				}
			}

			// Remove caravan
			m_map->remove_caravan( *it );
			(*it)->get_owner()->remove( *it );
		}
	}

	bool display_for_new_owner = ( get_owner() && !get_owner()->is_computer() );
	bool display_for_caravan_owner = false;

	if ( !display_for_new_owner )
	{
		t_player_set::const_iterator it;
		for ( it = caravan_owners.begin(); !display_for_caravan_owner && it != caravan_owners.end(); it++ )
		{
			if ( !(*it)->is_computer() )
				display_for_caravan_owner = true;
		}
	}

	if (   ( any_creatures || !heroes.empty() )
		&& (   ( display_for_new_owner )
		    || ( display_for_caravan_owner ) ) )
	{
		std::string text;

		t_hero_keyword_replacer replacer;

		replacer.add_keyword( "%town", get_name() );


		if ( !heroes.empty() )
		{
			for ( hero_iter = heroes.begin(); hero_iter != heroes.end(); hero_iter++ )
			{
				replacer.add_hero( hero_iter->hero );
			}

			if ( any_creatures )
			{
				if ( display_for_new_owner )
				{
					text = replacer( k_arrived_caravan_killed_both_victor );
				}
				else
				{
					text = replacer( k_arrived_caravan_killed_both_loser );
				}
			}
			else
			{
				if ( display_for_new_owner )
				{
					text = replacer( k_arrived_caravan_killed_heroes_victor );
				}
				else
				{
					text = replacer( k_arrived_caravan_killed_heroes_loser );
				}
			}
		}
		else
		{
			if ( display_for_new_owner )
			{
				text = replacer( k_arrived_caravan_killed_creatures_victor );
			}
			else
			{
				text = replacer( k_arrived_caravan_killed_creatures_loser );
			}
		}

		ok_dialog( text, true );
	}

	t_adventure_map_ptr map = get_map();

	// "Defeat" all the heroes 
	for ( hero_iter = heroes.begin(); hero_iter != heroes.end(); hero_iter++ )
	{
		// ??? Is this close enough?
		t_adv_map_point point = victorious_army->get_position();

		t_script_context_hero hero_context( map, hero_iter->hero, hero_iter->caravan,
											hero_iter->caravan->get_owner(),
											victorious_army, get_owner(),
											&point );

		hero_iter->hero->execute_script( k_hero_scriptable_lose_combat, hero_context );

		if ( map->is_game_over() )
			return;
	}
}

static t_external_string const k_caravan_incoming_warning_singular_today( "caravan.incoming_warning_singular_today" );
static t_external_string const k_caravan_incoming_warning_singular_tomorrow( "caravan.incoming_warning_singular_tomorrow" );
static t_external_string const k_caravan_incoming_warning_singular_future( "caravan.incoming_warning_singular_future" );
static t_external_string const k_caravan_incoming_warning_plural_today( "caravan.incoming_warning_plural_today" );
static t_external_string const k_caravan_incoming_warning_plural_tomorrow( "caravan.incoming_warning_plural_tomorrow" );
static t_external_string const k_caravan_incoming_warning_plural_future( "caravan.incoming_warning_plural_future" );

// -----------------------------------------------------------
// Warn a player who just captured a town that other player's 
// caravans are heading towards it and will appear soon...
// -----------------------------------------------------------
void t_town::warn_of_approaching_caravans()
{
	t_player * owner = get_owner();
	if ( owner && !owner->is_computer() )
	{
		t_caravan_set const & caravans = m_map->get_caravans();

		t_caravan_set::const_iterator it;

		int count = 0;
		int soonest_arrival = std::numeric_limits<int>::max();

		for ( it = caravans.get_lower_bound_of_destination( this ); it != caravans.end() && (*it)->get_destination().get() == this ; it++ )
		{
			if ( (*it)->get_owner() != owner )
			{
				count++;

				if ( (*it)->get_arrival_day() < soonest_arrival )
				{
					soonest_arrival = (*it)->get_arrival_day();
				}
			}
		}

		if ( count )
		{
			std::string text;

			if ( count == 1 )
			{
				if ( soonest_arrival == m_map->get_day() )
				{
					text = replace_keywords( k_caravan_incoming_warning_singular_today,
											 "%town", get_name() );
				}
				else if ( soonest_arrival == m_map->get_day() + 1 )
				{
					text = replace_keywords( k_caravan_incoming_warning_singular_tomorrow,
											 "%town", get_name() );
				}
				else
				{
					assert( soonest_arrival > m_map->get_day() );
					text = replace_keywords( k_caravan_incoming_warning_singular_future,
											 "%town", get_name(),
											 "%days", format_string( "%d", soonest_arrival - m_map->get_day() ) );
				}
			}
			else
			{
				if ( soonest_arrival == m_map->get_day() )
				{
					text = replace_keywords( k_caravan_incoming_warning_plural_today,
											 "%town", get_name(),
											 "%count", format_string( "%d", count ) );
				}
				else if ( soonest_arrival == m_map->get_day() + 1 )
				{
					text = replace_keywords( k_caravan_incoming_warning_plural_tomorrow,
											 "%town", get_name(),
											 "%count", format_string( "%d", count ) );
				}
				else
				{
					assert( soonest_arrival > m_map->get_day() );
					text = replace_keywords( k_caravan_incoming_warning_plural_future,
											 "%town", get_name(),
											 "%count", format_string( "%d", count ),
						                     "%days", format_string( "%d", soonest_arrival - m_map->get_day() ) );
				}
			}

			ok_dialog( text, true );
		}
	}
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_town::get_virtual_position(t_adv_map_point& point) const
{
	if ( find_adjacent_space( point, get_garrison() ) )
	{
		point.on_bridge = false;
		return true;
	}
	return false;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
inline t_town* t_town::get_town()
{
	return this;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
t_ai_town_data_cache* t_town::get_ai_town_data_cache() const
{
	return m_ai_town_data_cache_ptr.get();
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
float t_town::ai_value( t_adventure_ai const& ai, t_creature_array const& army, int move_cost ) const
{
	float value = 0.0f;

	t_player* owner = get_owner();
	t_player* attacker = army.get_owner();

	if (owner == NULL || (owner != attacker) )
	{
		if ( owner && attacker && (owner->get_team() == attacker->get_team()) )
			return 0.0f;

		if ( army.get_living_hero_count() == 0)
			return 0.0f;

		if (get_garrison().empty() || ai.impending_defeat())
		{
			return k_value_victory;
		}
		else
		{
			float value = value_of_combat( &army, &get_garrison() );
			
			if ( value >= 0.0f)
				value = k_value_victory;

			return value;
		}
	}
	else
	{
		// Motivate the army to resurrect it's heroes.  Giving the individual value of the heroes (tripled) + the value change for the army.
		bool dead_heroes = false;
		for (int i = 0; i < t_creature_array::k_size; i++)
		{
			if ((army[i].get_const_hero() != NULL) && army[i].is_dead())
			{
				value += army[i].get_const_hero()->ai_value_including_dead() * 3;
				dead_heroes = true;
			}
		}
		if (dead_heroes)
		{
			value += army.ai_value_including_dead() - army.ai_value();
		}

		if (m_ai_town_data_cache_ptr->is_threatened() && (m_ai_town_data_cache_ptr->get_value_drop() >= 1.0f))
		{
			if (owner->get_town_count() == 1)
				value = k_value_victory;
			else
				value += (k_value_victory / owner->get_town_count()) + value_of_reinforcement( this, get_garrison(), army );
		} 
		else 
		{
			float reinforcement_value = value_of_reinforcement( this, army, get_garrison() );
			if (reinforcement_value < (army.ai_value() / 4))
				return (reinforcement_value * 0.05f) + value;
			else
				return reinforcement_value + value;
		}
	}

	return value;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
float t_town::ai_value( bool demand_scaling ) const
{
	// Simple town value rating... computed using the total cost of all the structures in the town.
	float value = 0.0f;	
	for (t_town_building i = t_town_building(0); i < k_town_building_count; enum_incr(i))
	{
		if (has(i))
		{
			t_building_traits const& traits = get_traits( get_type(), i );
			t_material_array cost = traits.cost;
			if ( demand_scaling )
			{
				get_owner()->get_material_values().adjust_material_values( cost );
				value += cost.get_sum();
			}
			else 
			{
				value += cost[0];
				for (int j = 1; j < k_material_count; j++)
				{
					value += cost[j] * k_material_value[j];
				}
			}
		}
	}
	return value;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_town::on_begin_turn()
{
	t_creature_array::on_begin_turn();
	t_owned_adv_object::on_begin_turn();
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_town::on_end_turn()
{
	t_creature_array::on_end_turn();
	t_owned_adv_object::on_end_turn();
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
float t_town::ai_activation_value_drop( t_creature_array const& army ) const
{
	if (same_team( get_owner(), army.get_owner()) || empty())
		return 0.0f;

	return ::value_lost_in_combat( &army, this, true );
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_town::apply_building_delay()
{
	t_player* player = get_owner();

	if (player)
		m_building_delay = player->get_building_delay();
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
t_skill_mastery t_town::get_anti_stealth_level() const
{
	return t_creature_array::get_anti_stealth_level();
}

// ---------------------------------------------------------------
// select town in adventure frame
// ---------------------------------------------------------------
bool t_town::select()
{
	t_adventure_frame* frame = get_adventure_frame();

	if (frame == 0)
		return false;
	frame->select_town( this );
	return true;
}

// -----------------------------------------------------------
// outpost adventure object
// -----------------------------------------------------------
bool t_outpost::read_from_map( std::streambuf& buffer, t_progress_handler* )
{
	int const k_current_format_version = 1;

	int format_version = get< t_uint16 >( buffer );
	if ( format_version < 0 || format_version > k_current_format_version )
		return false;

	t_player_color new_owner_color;
	if ( format_version >= 1 )
	{
		new_owner_color = t_player_color( get< t_uint8 >( buffer ) );
		if ( new_owner_color < 0 || new_owner_color >= k_player_color_count )
			return false;
	}
	else
		new_owner_color = k_player_gray;

	set_player_color( new_owner_color );

	return true;
}

int t_outpost::get_version() const
{
	return 1;
}

bool t_outpost::read( std::streambuf&						stream, 
				      t_qualified_adv_object_type const&	type,
				      int									version )
{
	if (version < 1)
		return true;
	return t_owned_adv_object::read( stream, type, version );
}


